diff --git a/.DS_Store b/.DS_Store index 0109de6f..6f5f3cde 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/docs/assets/abbreviations.md b/docs/assets/abbreviations.md new file mode 100644 index 00000000..5ab9624b --- /dev/null +++ b/docs/assets/abbreviations.md @@ -0,0 +1,16 @@ +*[DCE]: Dynamic Contrast Enhanced +*[MR]: Magnetic Resonance +*[MRI]: Magnetic Resonance Imaging +*[RF]: Radio Frequency +*[SE]: Spin Echo +*[GE]: Gradient Echo +*[AIF]: Arterial Input Function +*[ROI]: Region Of Interest +*[EES]: Extravascular Extracellular Spaces +*[2CXM]: Two Compartment Exchange Model +*[2CU]: Two Compartment Uptake +*[SPGR]: Spoiled Gradient Echo +*[GRE]: Gradient Echo +*[BAT]: Bolus Arrival Time +*[SVD]: Singular Value Decomposition +*[DSC]: Dynamic Susceptibility Contrast \ No newline at end of file diff --git a/docs/index.md b/docs/index.md index 9b1654a2..e955c574 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,3 +1,13 @@ +--- +title: The OSIPI Contrast Agent Perfusion MRI Lexicon (CAPLEX) +description: Standardised definitions of quantities, models, and analysis processes for contrast-agent based perfusion MRI. +tags: + - MRI + - perfusion + - lexicon + - DCE + - DSC +--- # The OSIPI Contrast Agent Perfusion MRI Lexicon (CAPLEX) ## What is CAPLEX? @@ -17,3 +27,5 @@ There are several ways to join the effort: 1. Become a member of the task force: email **ben.dickie@manchester.ac.uk**. Anyone with relevant expertise can join - PhDs and postdocs working in DCE/DSC MRI field are particularly encouraged to apply. 2. To suggest edits to the lexicon, go to the CAPLEX Github page: https://github.com/OSIPI/OSIPI_CAPLEX and follow the instructions on how to contribute in the README.md. Additionally, a tutorial on how to contribute can be found [here](contributionTutorial.md). + +For tips on using the search bar, see the [Search Help](search-help.md) page. diff --git a/docs/javascripts/extra.js b/docs/javascripts/extra.js index 4e2b811e..c5191b47 100644 --- a/docs/javascripts/extra.js +++ b/docs/javascripts/extra.js @@ -43,3 +43,222 @@ buttons.forEach(button => { //copyURLToClip(button.id) }) }) + +// --- Search Enhancement: Abbreviation/Synonym Expansion & Fuzzy Suggestion --- +document.addEventListener('DOMContentLoaded', function() { + const searchInput = document.querySelector('input.md-search__input'); + if (!searchInput) return; + + // Create suggestion element + let suggestionDiv = document.createElement('div'); + suggestionDiv.style.display = 'none'; + suggestionDiv.style.position = 'absolute'; + suggestionDiv.style.background = '#fffbe6'; + suggestionDiv.style.border = '1px solid #ffe066'; + suggestionDiv.style.padding = '4px 8px'; + suggestionDiv.style.marginTop = '2px'; + suggestionDiv.style.fontSize = '0.95em'; + suggestionDiv.style.cursor = 'pointer'; + suggestionDiv.style.zIndex = 1000; + suggestionDiv.setAttribute('id', 'search-did-you-mean'); + searchInput.parentNode.style.position = 'relative'; + searchInput.parentNode.appendChild(suggestionDiv); + + fetch('assets/abbreviations.md') + .then(response => response.text()) + .then(text => { + const abbrMap = {}; + text.split('\n').forEach(line => { + const match = line.match(/^\*\[(.+?)\]:\s+(.+)/); + if (match) abbrMap[match[1].toLowerCase()] = match[2].toLowerCase(); + }); + function levenshtein(a, b) { + const matrix = Array.from({length: a.length+1}, () => Array(b.length+1).fill(0)); + for (let i = 0; i <= a.length; i++) matrix[i][0] = i; + for (let j = 0; j <= b.length; j++) matrix[0][j] = j; + for (let i = 1; i <= a.length; i++) { + for (let j = 1; j <= b.length; j++) { + matrix[i][j] = Math.min( + matrix[i-1][j] + 1, + matrix[i][j-1] + 1, + matrix[i-1][j-1] + (a[i-1] === b[j-1] ? 0 : 1) + ); + } + } + return matrix[a.length][b.length]; + } + searchInput.addEventListener('input', function(e) { + const val = e.target.value.trim().toLowerCase(); + suggestionDiv.style.display = 'none'; + suggestionDiv.textContent = ''; + if (!val) return; + if (abbrMap[val]) { + e.target.value = val + ' ' + abbrMap[val]; + } + const abbrs = Object.keys(abbrMap); + let minDist = Infinity, closest = ''; + abbrs.forEach(abbr => { + const dist = levenshtein(val, abbr); + if (dist < minDist && dist <= 2) { minDist = dist; closest = abbr; } + }); + if (closest && closest !== val) { + suggestionDiv.textContent = `Did you mean: ${closest}?`; + suggestionDiv.style.display = 'block'; + suggestionDiv.onclick = function() { + searchInput.value = closest; + suggestionDiv.style.display = 'none'; + searchInput.dispatchEvent(new Event('input')); + }; + } + }); + // Hide suggestion on blur + searchInput.addEventListener('blur', function() { + setTimeout(() => { suggestionDiv.style.display = 'none'; }, 200); + }); + }); +}); +// --- End Search Enhancement --- + +// --- Simple Did You Mean Suggestion Above Search Bar --- +document.addEventListener('DOMContentLoaded', function() { + const searchInput = document.querySelector('input.md-search__input'); + if (!searchInput) return; + + // Create suggestion element above the search bar + let didYouMeanBox = document.createElement('div'); + didYouMeanBox.className = 'did-you-mean-box'; + didYouMeanBox.style.display = 'none'; + didYouMeanBox.style.background = '#fffbe6'; + didYouMeanBox.style.border = '1px solid #ffe066'; + didYouMeanBox.style.padding = '6px 12px'; + didYouMeanBox.style.marginBottom = '8px'; + didYouMeanBox.style.fontSize = '1em'; + didYouMeanBox.style.cursor = 'pointer'; + didYouMeanBox.style.borderRadius = '4px'; + didYouMeanBox.style.maxWidth = '400px'; + didYouMeanBox.style.position = 'relative'; + // Insert above the search input + searchInput.parentNode.insertBefore(didYouMeanBox, searchInput); + + fetch('includes/abbreviations.md') + .then(response => response.text()) + .then(text => { + const abbrMap = {}; + text.split('\n').forEach(line => { + const match = line.match(/^\*\[(.+?)\]:\s+(.+)/); + if (match) abbrMap[match[1].toLowerCase()] = match[2].toLowerCase(); + }); + function levenshtein(a, b) { + const matrix = Array.from({length: a.length+1}, () => Array(b.length+1).fill(0)); + for (let i = 0; i <= a.length; i++) matrix[i][0] = i; + for (let j = 0; j <= b.length; j++) matrix[0][j] = j; + for (let i = 1; i <= a.length; i++) { + for (let j = 1; j <= b.length; j++) { + matrix[i][j] = Math.min( + matrix[i-1][j] + 1, + matrix[i][j-1] + 1, + matrix[i-1][j-1] + (a[i-1] === b[j-1] ? 0 : 1) + ); + } + } + return matrix[a.length][b.length]; + } + searchInput.addEventListener('input', function(e) { + const val = e.target.value.trim().toLowerCase(); + didYouMeanBox.style.display = 'none'; + didYouMeanBox.textContent = ''; + if (!val) return; + const abbrs = Object.keys(abbrMap); + let minDist = Infinity, closest = ''; + abbrs.forEach(abbr => { + const dist = levenshtein(val, abbr); + if (dist < minDist && dist <= 2) { minDist = dist; closest = abbr; } + }); + if (closest && closest !== val) { + didYouMeanBox.textContent = `Did you mean: ${closest}? (Click to search)`; + didYouMeanBox.style.display = 'block'; + didYouMeanBox.onclick = function() { + searchInput.value = closest; + searchInput.dispatchEvent(new Event('input')); + didYouMeanBox.style.display = 'none'; + }; + } + }); + // Hide suggestion on blur + searchInput.addEventListener('blur', function() { + setTimeout(() => { didYouMeanBox.style.display = 'none'; }, 200); + }); + }); +}); +// --- End Simple Did You Mean Suggestion --- + +// --- Search Result Prioritization for Table Rows and Notation Matching --- +function normalizeNotation(str) { + // Remove HTML tags, sub/superscripts, and make lowercase + if (!str) return ''; + // Remove LaTeX $...$ and HTML tags + str = str.replace(/\$[^$]*\$/g, ''); + str = str.replace(/<[^>]+>/g, ''); + // Remove subscript/superscript unicode (optional, for robustness) + str = str.replace(/[\u2070-\u209F]/g, ''); + // Remove non-alphanumeric + str = str.replace(/[^a-zA-Z0-9]/g, ''); + return str.toLowerCase(); +} + +function isTableRowResult(result) { + // Heuristic: Table rows have OSIPI code patterns and Notation fields + // e.g., location includes 'quantities', 'perfusionModels', 'perfusionProcesses', 'generalPurposeProcesses', etc. + if (!result || !result.location) return false; + return /quantities|perfusionModels|perfusionProcesses|generalPurposeProcesses|derivedProcesses/.test(result.location); +} + +function extractNotation(result) { + // Try to extract Notation from the summary or text + if (!result || !result.summary) return ''; + // Look for Notation: ... or | ... | ... | Notation | ... | + // Try to extract the Notation column from a markdown table row + const notationMatch = result.summary.match(/\|[^|]*\|[^|]*\|[^|]*\|([^|]*)\|/); + if (notationMatch && notationMatch[1]) { + return notationMatch[1].trim(); + } + // Fallback: look for Notation: ... + const altMatch = result.summary.match(/Notation:?\s*([^|\n]+)/i); + if (altMatch && altMatch[1]) { + return altMatch[1].trim(); + } + return ''; +} + +// Patch displayResults to prioritize table rows and Notation matches +const origDisplayResults = window.displayResults || displayResults; +window.displayResults = function(results) { + const searchInput = document.querySelector('input.md-search__input') || document.getElementById('mkdocs-search-query'); + const query = searchInput ? searchInput.value.trim().toLowerCase() : ''; + const normQuery = normalizeNotation(query); + + // Partition results + let tableMatches = [], notationMatches = [], otherResults = []; + results.forEach(result => { + if (isTableRowResult(result)) { + // Check OSIPI name or Notation match + const notation = extractNotation(result); + const normNotation = normalizeNotation(notation); + // Also check title for OSIPI name match + const normTitle = normalizeNotation(result.title); + if (normNotation && normQuery && normNotation.includes(normQuery)) { + notationMatches.push(result); + } else if (normTitle && normQuery && normTitle.includes(normQuery)) { + notationMatches.push(result); + } else { + tableMatches.push(result); + } + } else { + otherResults.push(result); + } + }); + // Order: Notation matches > Table matches > Other + const finalResults = notationMatches.concat(tableMatches, otherResults); + origDisplayResults(finalResults); +}; +// --- End Search Result Prioritization --- diff --git a/docs/qualityOfLife.md b/docs/qualityOfLife.md index c32a67d0..006b7d80 100644 --- a/docs/qualityOfLife.md +++ b/docs/qualityOfLife.md @@ -1,3 +1,13 @@ +--- +title: Quality of Life Additions +description: Tools and features to improve user experience on the CAPLEX website. +tags: + - usability + - search + - abbreviations + - help +--- + ## Quality of life additions This section will introduce you to some tools available on this website to make your experience better. @@ -20,6 +30,8 @@ You can right-click on any [link](#copying-reference-doi), such as a reference t At the top of the webpage, you may find the `Search` bar allowing you to find resources within the website quickly. If you wish to find a table row by code, enclose the code using `""`, for example to find Gradient echo model you could search for `"M.SM1.001"`. +**See the [Search Help](search-help.md) page for tips on getting the best results.** + !!! warning The greek letters rendered using MathJax cannot be searched for at the moment. diff --git a/docs/search-help.md b/docs/search-help.md new file mode 100644 index 00000000..27cd6161 --- /dev/null +++ b/docs/search-help.md @@ -0,0 +1,47 @@ +# Search Help + +Welcome to the CAPLEX Search Help page! Here you'll find tips and features to help you get the most out of the search functionality. + +## Summary Table: What Each Change Does + +| Feature | User Benefit | +|------------------------|----------------------------------------------| +| Page metadata | More relevant, ranked search results | +| Abbreviations/synonyms | Find results for all common terms | +| Search result previews | See context for each result | +| Highlighting | Instantly spot search terms in results | +| Did you mean/fuzzy | Find results even with typos or misspellings | +| Search help page | Users know how to get the best results | + +## How to Use Search + +- Use the search bar at the top of any page to quickly find terms, models, or processes. +- Enclose codes in quotes (e.g., `"M.SM1.001"`) to find specific table rows. +- Search is typo-tolerant and will suggest corrections for common misspellings. +- Results show a preview/snippet for context. +- Search terms are highlighted in the results. + +## Abbreviations and Synonyms + +- Common abbreviations (e.g., DCE, MRI, ROI) are recognized. Hover over underlined abbreviations for definitions. +- Synonyms and alternative names are indexed—try searching for any common term. + +## Fuzzy Search and "Did You Mean" + +- If you make a typo, the search will suggest the closest matching term. +- Try partial words or common misspellings—relevant results will still appear. + +## Search Result Previews + +- Each result includes a snippet of the surrounding text for context. +- Click a result to jump directly to the relevant section. + +## Highlighting + +- Your search terms are highlighted in the results for easy scanning. + +## More Tips + +- For best results, use specific terms or codes. +- If you can't find what you're looking for, try a synonym or abbreviation. +- For more help, see the [Quality of Life](qualityOfLife.md) page or [Style Guide](style-guide.md). \ No newline at end of file diff --git a/docs/stylesheets/extra.css b/docs/stylesheets/extra.css index 3cac2b08..e50ee997 100644 --- a/docs/stylesheets/extra.css +++ b/docs/stylesheets/extra.css @@ -1,22 +1,29 @@ -/* Force the table to use the full page width, may cause issues on mobile */ -.md-typeset__table { - width: 100%; -} - -.md-typeset__table table:not([class]) { - display: table -} -/* This automatically stretches the site contents to monitor width*/ -.md-grid { - max-width: initial; -} -/* Preserve words so they are not broken in the middle on newlines */ -.md-typeset td { - overflow-wrap: break-word; -} -/* Control hyperlink button properties */ -.md-button.md-button--hyperlink { - /* top | right | bottom | left */ - padding: 3px 2px; - font-size: 12px; -} \ No newline at end of file +/* Force the table to use the full page width, may cause issues on mobile */ +.md-typeset__table { + width: 100%; +} + +.md-typeset__table table:not([class]) { + display: table; +} +/* This automatically stretches the site contents to monitor width*/ +.md-grid { + max-width: initial; +} +/* Preserve words so they are not broken in the middle on newlines */ +.md-typeset td { + overflow-wrap: break-word; +} +/* Control hyperlink button properties */ +.md-button.md-button--hyperlink { + /* top | right | bottom | left */ + padding: 3px 2px; + font-size: 12px; +} +/* docs/stylesheets/extra.css */ +.md-search-result__highlight { + background-color: #ffe066; + color: #222; + padding: 0 2px; + border-radius: 2px; +} diff --git a/mkdocs.yml b/mkdocs.yml index 7d00a996..85f314f2 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -9,6 +9,10 @@ copyright: > theme: name: material + features: + - search.suggest + - search.highlight + - search.share logo: osipiImgs/OSIPI_logo.png favicon: osipiImgs/OSIPI_logo_only_square.png palette: @@ -28,6 +32,8 @@ theme: name: Switch to light mode features: - search.suggest + - search.highlight + - search.share icon: admonition: note: fontawesome/solid/note-sticky @@ -62,7 +68,14 @@ nav: plugins: - search: lang: en - separator: '[\s\-,:!=\[\]()"/]+|(?!\b)(?=[A-Z][a-z])|\.(?!\d)|&[lg]t;' + separator: '[\s\-\.,:;!?\[\]\(\)"/\*\^_~\+]+|(?<=[a-z])(?=[A-Z])' + prebuild_index: true + min_search_length: 2 + indexing: full + boost: 7 + pipeline: + - stopWordFilter + markdown_extensions: - toc: diff --git a/venv/bin/Activate.ps1 b/venv/bin/Activate.ps1 new file mode 100644 index 00000000..b49d77ba --- /dev/null +++ b/venv/bin/Activate.ps1 @@ -0,0 +1,247 @@ +<# +.Synopsis +Activate a Python virtual environment for the current PowerShell session. + +.Description +Pushes the python executable for a virtual environment to the front of the +$Env:PATH environment variable and sets the prompt to signify that you are +in a Python virtual environment. Makes use of the command line switches as +well as the `pyvenv.cfg` file values present in the virtual environment. + +.Parameter VenvDir +Path to the directory that contains the virtual environment to activate. The +default value for this is the parent of the directory that the Activate.ps1 +script is located within. + +.Parameter Prompt +The prompt prefix to display when this virtual environment is activated. By +default, this prompt is the name of the virtual environment folder (VenvDir) +surrounded by parentheses and followed by a single space (ie. '(.venv) '). + +.Example +Activate.ps1 +Activates the Python virtual environment that contains the Activate.ps1 script. + +.Example +Activate.ps1 -Verbose +Activates the Python virtual environment that contains the Activate.ps1 script, +and shows extra information about the activation as it executes. + +.Example +Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv +Activates the Python virtual environment located in the specified location. + +.Example +Activate.ps1 -Prompt "MyPython" +Activates the Python virtual environment that contains the Activate.ps1 script, +and prefixes the current prompt with the specified string (surrounded in +parentheses) while the virtual environment is active. + +.Notes +On Windows, it may be required to enable this Activate.ps1 script by setting the +execution policy for the user. You can do this by issuing the following PowerShell +command: + +PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser + +For more information on Execution Policies: +https://go.microsoft.com/fwlink/?LinkID=135170 + +#> +Param( + [Parameter(Mandatory = $false)] + [String] + $VenvDir, + [Parameter(Mandatory = $false)] + [String] + $Prompt +) + +<# Function declarations --------------------------------------------------- #> + +<# +.Synopsis +Remove all shell session elements added by the Activate script, including the +addition of the virtual environment's Python executable from the beginning of +the PATH variable. + +.Parameter NonDestructive +If present, do not remove this function from the global namespace for the +session. + +#> +function global:deactivate ([switch]$NonDestructive) { + # Revert to original values + + # The prior prompt: + if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) { + Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt + Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT + } + + # The prior PYTHONHOME: + if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) { + Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME + Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME + } + + # The prior PATH: + if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) { + Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH + Remove-Item -Path Env:_OLD_VIRTUAL_PATH + } + + # Just remove the VIRTUAL_ENV altogether: + if (Test-Path -Path Env:VIRTUAL_ENV) { + Remove-Item -Path env:VIRTUAL_ENV + } + + # Just remove VIRTUAL_ENV_PROMPT altogether. + if (Test-Path -Path Env:VIRTUAL_ENV_PROMPT) { + Remove-Item -Path env:VIRTUAL_ENV_PROMPT + } + + # Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether: + if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) { + Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force + } + + # Leave deactivate function in the global namespace if requested: + if (-not $NonDestructive) { + Remove-Item -Path function:deactivate + } +} + +<# +.Description +Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the +given folder, and returns them in a map. + +For each line in the pyvenv.cfg file, if that line can be parsed into exactly +two strings separated by `=` (with any amount of whitespace surrounding the =) +then it is considered a `key = value` line. The left hand string is the key, +the right hand is the value. + +If the value starts with a `'` or a `"` then the first and last character is +stripped from the value before being captured. + +.Parameter ConfigDir +Path to the directory that contains the `pyvenv.cfg` file. +#> +function Get-PyVenvConfig( + [String] + $ConfigDir +) { + Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg" + + # Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue). + $pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue + + # An empty map will be returned if no config file is found. + $pyvenvConfig = @{ } + + if ($pyvenvConfigPath) { + + Write-Verbose "File exists, parse `key = value` lines" + $pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath + + $pyvenvConfigContent | ForEach-Object { + $keyval = $PSItem -split "\s*=\s*", 2 + if ($keyval[0] -and $keyval[1]) { + $val = $keyval[1] + + # Remove extraneous quotations around a string value. + if ("'""".Contains($val.Substring(0, 1))) { + $val = $val.Substring(1, $val.Length - 2) + } + + $pyvenvConfig[$keyval[0]] = $val + Write-Verbose "Adding Key: '$($keyval[0])'='$val'" + } + } + } + return $pyvenvConfig +} + + +<# Begin Activate script --------------------------------------------------- #> + +# Determine the containing directory of this script +$VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition +$VenvExecDir = Get-Item -Path $VenvExecPath + +Write-Verbose "Activation script is located in path: '$VenvExecPath'" +Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)" +Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)" + +# Set values required in priority: CmdLine, ConfigFile, Default +# First, get the location of the virtual environment, it might not be +# VenvExecDir if specified on the command line. +if ($VenvDir) { + Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values" +} +else { + Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir." + $VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/") + Write-Verbose "VenvDir=$VenvDir" +} + +# Next, read the `pyvenv.cfg` file to determine any required value such +# as `prompt`. +$pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir + +# Next, set the prompt from the command line, or the config file, or +# just use the name of the virtual environment folder. +if ($Prompt) { + Write-Verbose "Prompt specified as argument, using '$Prompt'" +} +else { + Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value" + if ($pyvenvCfg -and $pyvenvCfg['prompt']) { + Write-Verbose " Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'" + $Prompt = $pyvenvCfg['prompt']; + } + else { + Write-Verbose " Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virtual environment)" + Write-Verbose " Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'" + $Prompt = Split-Path -Path $venvDir -Leaf + } +} + +Write-Verbose "Prompt = '$Prompt'" +Write-Verbose "VenvDir='$VenvDir'" + +# Deactivate any currently active virtual environment, but leave the +# deactivate function in place. +deactivate -nondestructive + +# Now set the environment variable VIRTUAL_ENV, used by many tools to determine +# that there is an activated venv. +$env:VIRTUAL_ENV = $VenvDir + +if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) { + + Write-Verbose "Setting prompt to '$Prompt'" + + # Set the prompt to include the env name + # Make sure _OLD_VIRTUAL_PROMPT is global + function global:_OLD_VIRTUAL_PROMPT { "" } + Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT + New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt + + function global:prompt { + Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) " + _OLD_VIRTUAL_PROMPT + } + $env:VIRTUAL_ENV_PROMPT = $Prompt +} + +# Clear PYTHONHOME +if (Test-Path -Path Env:PYTHONHOME) { + Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME + Remove-Item -Path Env:PYTHONHOME +} + +# Add the venv to the PATH +Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH +$Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH" diff --git a/venv/bin/activate b/venv/bin/activate new file mode 100644 index 00000000..3424cf57 --- /dev/null +++ b/venv/bin/activate @@ -0,0 +1,70 @@ +# This file must be used with "source bin/activate" *from bash* +# You cannot run it directly + +deactivate () { + # reset old environment variables + if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then + PATH="${_OLD_VIRTUAL_PATH:-}" + export PATH + unset _OLD_VIRTUAL_PATH + fi + if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then + PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}" + export PYTHONHOME + unset _OLD_VIRTUAL_PYTHONHOME + fi + + # Call hash to forget past commands. Without forgetting + # past commands the $PATH changes we made may not be respected + hash -r 2> /dev/null + + if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then + PS1="${_OLD_VIRTUAL_PS1:-}" + export PS1 + unset _OLD_VIRTUAL_PS1 + fi + + unset VIRTUAL_ENV + unset VIRTUAL_ENV_PROMPT + if [ ! "${1:-}" = "nondestructive" ] ; then + # Self destruct! + unset -f deactivate + fi +} + +# unset irrelevant variables +deactivate nondestructive + +# on Windows, a path can contain colons and backslashes and has to be converted: +if [ "${OSTYPE:-}" = "cygwin" ] || [ "${OSTYPE:-}" = "msys" ] ; then + # transform D:\path\to\venv to /d/path/to/venv on MSYS + # and to /cygdrive/d/path/to/venv on Cygwin + export VIRTUAL_ENV=$(cygpath "/Users/yuvraaj/Documents/OSIPI_CAPLEX/venv") +else + # use the path as-is + export VIRTUAL_ENV="/Users/yuvraaj/Documents/OSIPI_CAPLEX/venv" +fi + +_OLD_VIRTUAL_PATH="$PATH" +PATH="$VIRTUAL_ENV/bin:$PATH" +export PATH + +# unset PYTHONHOME if set +# this will fail if PYTHONHOME is set to the empty string (which is bad anyway) +# could use `if (set -u; : $PYTHONHOME) ;` in bash +if [ -n "${PYTHONHOME:-}" ] ; then + _OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}" + unset PYTHONHOME +fi + +if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then + _OLD_VIRTUAL_PS1="${PS1:-}" + PS1="(venv) ${PS1:-}" + export PS1 + VIRTUAL_ENV_PROMPT="(venv) " + export VIRTUAL_ENV_PROMPT +fi + +# Call hash to forget past commands. Without forgetting +# past commands the $PATH changes we made may not be respected +hash -r 2> /dev/null diff --git a/venv/bin/activate.csh b/venv/bin/activate.csh new file mode 100644 index 00000000..dd71279b --- /dev/null +++ b/venv/bin/activate.csh @@ -0,0 +1,27 @@ +# This file must be used with "source bin/activate.csh" *from csh*. +# You cannot run it directly. + +# Created by Davide Di Blasi . +# Ported to Python 3.3 venv by Andrew Svetlov + +alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; unsetenv VIRTUAL_ENV_PROMPT; test "\!:*" != "nondestructive" && unalias deactivate' + +# Unset irrelevant variables. +deactivate nondestructive + +setenv VIRTUAL_ENV "/Users/yuvraaj/Documents/OSIPI_CAPLEX/venv" + +set _OLD_VIRTUAL_PATH="$PATH" +setenv PATH "$VIRTUAL_ENV/bin:$PATH" + + +set _OLD_VIRTUAL_PROMPT="$prompt" + +if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then + set prompt = "(venv) $prompt" + setenv VIRTUAL_ENV_PROMPT "(venv) " +endif + +alias pydoc python -m pydoc + +rehash diff --git a/venv/bin/activate.fish b/venv/bin/activate.fish new file mode 100644 index 00000000..791a17aa --- /dev/null +++ b/venv/bin/activate.fish @@ -0,0 +1,69 @@ +# This file must be used with "source /bin/activate.fish" *from fish* +# (https://fishshell.com/). You cannot run it directly. + +function deactivate -d "Exit virtual environment and return to normal shell environment" + # reset old environment variables + if test -n "$_OLD_VIRTUAL_PATH" + set -gx PATH $_OLD_VIRTUAL_PATH + set -e _OLD_VIRTUAL_PATH + end + if test -n "$_OLD_VIRTUAL_PYTHONHOME" + set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME + set -e _OLD_VIRTUAL_PYTHONHOME + end + + if test -n "$_OLD_FISH_PROMPT_OVERRIDE" + set -e _OLD_FISH_PROMPT_OVERRIDE + # prevents error when using nested fish instances (Issue #93858) + if functions -q _old_fish_prompt + functions -e fish_prompt + functions -c _old_fish_prompt fish_prompt + functions -e _old_fish_prompt + end + end + + set -e VIRTUAL_ENV + set -e VIRTUAL_ENV_PROMPT + if test "$argv[1]" != "nondestructive" + # Self-destruct! + functions -e deactivate + end +end + +# Unset irrelevant variables. +deactivate nondestructive + +set -gx VIRTUAL_ENV "/Users/yuvraaj/Documents/OSIPI_CAPLEX/venv" + +set -gx _OLD_VIRTUAL_PATH $PATH +set -gx PATH "$VIRTUAL_ENV/bin" $PATH + +# Unset PYTHONHOME if set. +if set -q PYTHONHOME + set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME + set -e PYTHONHOME +end + +if test -z "$VIRTUAL_ENV_DISABLE_PROMPT" + # fish uses a function instead of an env var to generate the prompt. + + # Save the current fish_prompt function as the function _old_fish_prompt. + functions -c fish_prompt _old_fish_prompt + + # With the original prompt function renamed, we can override with our own. + function fish_prompt + # Save the return status of the last command. + set -l old_status $status + + # Output the venv prompt; color taken from the blue of the Python logo. + printf "%s%s%s" (set_color 4B8BBE) "(venv) " (set_color normal) + + # Restore the return status of the previous command. + echo "exit $old_status" | . + # Output the original/"old" prompt. + _old_fish_prompt + end + + set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV" + set -gx VIRTUAL_ENV_PROMPT "(venv) " +end diff --git a/venv/bin/ghp-import b/venv/bin/ghp-import new file mode 100755 index 00000000..fddbd689 --- /dev/null +++ b/venv/bin/ghp-import @@ -0,0 +1,8 @@ +#!/Users/yuvraaj/Documents/OSIPI_CAPLEX/venv/bin/python3.12 +# -*- coding: utf-8 -*- +import re +import sys +from ghp_import import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv/bin/markdown_py b/venv/bin/markdown_py new file mode 100755 index 00000000..4ca0d50a --- /dev/null +++ b/venv/bin/markdown_py @@ -0,0 +1,8 @@ +#!/Users/yuvraaj/Documents/OSIPI_CAPLEX/venv/bin/python3.12 +# -*- coding: utf-8 -*- +import re +import sys +from markdown.__main__ import run +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(run()) diff --git a/venv/bin/mkdocs b/venv/bin/mkdocs new file mode 100755 index 00000000..7cadaa67 --- /dev/null +++ b/venv/bin/mkdocs @@ -0,0 +1,8 @@ +#!/Users/yuvraaj/Documents/OSIPI_CAPLEX/venv/bin/python3.12 +# -*- coding: utf-8 -*- +import re +import sys +from mkdocs.__main__ import cli +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(cli()) diff --git a/venv/bin/mkdocs-get-deps b/venv/bin/mkdocs-get-deps new file mode 100755 index 00000000..55df8853 --- /dev/null +++ b/venv/bin/mkdocs-get-deps @@ -0,0 +1,8 @@ +#!/Users/yuvraaj/Documents/OSIPI_CAPLEX/venv/bin/python3.12 +# -*- coding: utf-8 -*- +import re +import sys +from mkdocs_get_deps.__main__ import cli +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(cli()) diff --git a/venv/bin/normalizer b/venv/bin/normalizer new file mode 100755 index 00000000..0039c5fb --- /dev/null +++ b/venv/bin/normalizer @@ -0,0 +1,8 @@ +#!/Users/yuvraaj/Documents/OSIPI_CAPLEX/venv/bin/python3.12 +# -*- coding: utf-8 -*- +import re +import sys +from charset_normalizer import cli +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(cli.cli_detect()) diff --git a/venv/bin/pip b/venv/bin/pip new file mode 100755 index 00000000..1a34991e --- /dev/null +++ b/venv/bin/pip @@ -0,0 +1,8 @@ +#!/Users/yuvraaj/Documents/OSIPI_CAPLEX/venv/bin/python3.12 +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv/bin/pip3 b/venv/bin/pip3 new file mode 100755 index 00000000..1a34991e --- /dev/null +++ b/venv/bin/pip3 @@ -0,0 +1,8 @@ +#!/Users/yuvraaj/Documents/OSIPI_CAPLEX/venv/bin/python3.12 +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv/bin/pip3.12 b/venv/bin/pip3.12 new file mode 100755 index 00000000..1a34991e --- /dev/null +++ b/venv/bin/pip3.12 @@ -0,0 +1,8 @@ +#!/Users/yuvraaj/Documents/OSIPI_CAPLEX/venv/bin/python3.12 +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv/bin/pybabel b/venv/bin/pybabel new file mode 100755 index 00000000..ade3d5b5 --- /dev/null +++ b/venv/bin/pybabel @@ -0,0 +1,8 @@ +#!/Users/yuvraaj/Documents/OSIPI_CAPLEX/venv/bin/python3.12 +# -*- coding: utf-8 -*- +import re +import sys +from babel.messages.frontend import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv/bin/pygmentize b/venv/bin/pygmentize new file mode 100755 index 00000000..e28becba --- /dev/null +++ b/venv/bin/pygmentize @@ -0,0 +1,8 @@ +#!/Users/yuvraaj/Documents/OSIPI_CAPLEX/venv/bin/python3.12 +# -*- coding: utf-8 -*- +import re +import sys +from pygments.cmdline import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv/bin/python b/venv/bin/python new file mode 120000 index 00000000..11b9d885 --- /dev/null +++ b/venv/bin/python @@ -0,0 +1 @@ +python3.12 \ No newline at end of file diff --git a/venv/bin/python3 b/venv/bin/python3 new file mode 120000 index 00000000..11b9d885 --- /dev/null +++ b/venv/bin/python3 @@ -0,0 +1 @@ +python3.12 \ No newline at end of file diff --git a/venv/bin/python3.12 b/venv/bin/python3.12 new file mode 120000 index 00000000..a7a5fcca --- /dev/null +++ b/venv/bin/python3.12 @@ -0,0 +1 @@ +/Library/Frameworks/Python.framework/Versions/3.12/bin/python3.12 \ No newline at end of file diff --git a/venv/bin/watchmedo b/venv/bin/watchmedo new file mode 100755 index 00000000..e0ca55df --- /dev/null +++ b/venv/bin/watchmedo @@ -0,0 +1,8 @@ +#!/Users/yuvraaj/Documents/OSIPI_CAPLEX/venv/bin/python3.12 +# -*- coding: utf-8 -*- +import re +import sys +from watchdog.watchmedo import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv/lib/python3.12/site-packages/MarkupSafe-3.0.2.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/MarkupSafe-3.0.2.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/MarkupSafe-3.0.2.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.12/site-packages/MarkupSafe-3.0.2.dist-info/LICENSE.txt b/venv/lib/python3.12/site-packages/MarkupSafe-3.0.2.dist-info/LICENSE.txt new file mode 100644 index 00000000..9d227a0c --- /dev/null +++ b/venv/lib/python3.12/site-packages/MarkupSafe-3.0.2.dist-info/LICENSE.txt @@ -0,0 +1,28 @@ +Copyright 2010 Pallets + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/lib/python3.12/site-packages/MarkupSafe-3.0.2.dist-info/METADATA b/venv/lib/python3.12/site-packages/MarkupSafe-3.0.2.dist-info/METADATA new file mode 100644 index 00000000..82261f2a --- /dev/null +++ b/venv/lib/python3.12/site-packages/MarkupSafe-3.0.2.dist-info/METADATA @@ -0,0 +1,92 @@ +Metadata-Version: 2.1 +Name: MarkupSafe +Version: 3.0.2 +Summary: Safely add untrusted strings to HTML/XML markup. +Maintainer-email: Pallets +License: Copyright 2010 Pallets + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Documentation, https://markupsafe.palletsprojects.com/ +Project-URL: Changes, https://markupsafe.palletsprojects.com/changes/ +Project-URL: Source, https://github.com/pallets/markupsafe/ +Project-URL: Chat, https://discord.gg/pallets +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Text Processing :: Markup :: HTML +Classifier: Typing :: Typed +Requires-Python: >=3.9 +Description-Content-Type: text/markdown +License-File: LICENSE.txt + +# MarkupSafe + +MarkupSafe implements a text object that escapes characters so it is +safe to use in HTML and XML. Characters that have special meanings are +replaced so that they display as the actual characters. This mitigates +injection attacks, meaning untrusted user input can safely be displayed +on a page. + + +## Examples + +```pycon +>>> from markupsafe import Markup, escape + +>>> # escape replaces special characters and wraps in Markup +>>> escape("") +Markup('<script>alert(document.cookie);</script>') + +>>> # wrap in Markup to mark text "safe" and prevent escaping +>>> Markup("Hello") +Markup('hello') + +>>> escape(Markup("Hello")) +Markup('hello') + +>>> # Markup is a str subclass +>>> # methods and operators escape their arguments +>>> template = Markup("Hello {name}") +>>> template.format(name='"World"') +Markup('Hello "World"') +``` + +## Donate + +The Pallets organization develops and supports MarkupSafe and other +popular packages. In order to grow the community of contributors and +users, and allow the maintainers to devote more time to the projects, +[please donate today][]. + +[please donate today]: https://palletsprojects.com/donate diff --git a/venv/lib/python3.12/site-packages/MarkupSafe-3.0.2.dist-info/RECORD b/venv/lib/python3.12/site-packages/MarkupSafe-3.0.2.dist-info/RECORD new file mode 100644 index 00000000..6393e1d6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/MarkupSafe-3.0.2.dist-info/RECORD @@ -0,0 +1,14 @@ +MarkupSafe-3.0.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +MarkupSafe-3.0.2.dist-info/LICENSE.txt,sha256=SJqOEQhQntmKN7uYPhHg9-HTHwvY-Zp5yESOf_N9B-o,1475 +MarkupSafe-3.0.2.dist-info/METADATA,sha256=aAwbZhSmXdfFuMM-rEHpeiHRkBOGESyVLJIuwzHP-nw,3975 +MarkupSafe-3.0.2.dist-info/RECORD,, +MarkupSafe-3.0.2.dist-info/WHEEL,sha256=T94HOVPNbYE6jyG6QmiIglWJ01nwJvHIWFgubY8hhjc,109 +MarkupSafe-3.0.2.dist-info/top_level.txt,sha256=qy0Plje5IJuvsCBjejJyhDCjEAdcDLK_2agVcex8Z6U,11 +markupsafe/__init__.py,sha256=sr-U6_27DfaSrj5jnHYxWN-pvhM27sjlDplMDPZKm7k,13214 +markupsafe/__pycache__/__init__.cpython-312.pyc,, +markupsafe/__pycache__/_native.cpython-312.pyc,, +markupsafe/_native.py,sha256=hSLs8Jmz5aqayuengJJ3kdT5PwNpBWpKrmQSdipndC8,210 +markupsafe/_speedups.c,sha256=O7XulmTo-epI6n2FtMVOrJXl8EAaIwD2iNYmBI5SEoQ,4149 +markupsafe/_speedups.cpython-312-darwin.so,sha256=LAX7ul6PQsfO_zZxF631eSzN6U7PoGVTR_Wwd2pGav8,50624 +markupsafe/_speedups.pyi,sha256=ENd1bYe7gbBUf2ywyYWOGUpnXOHNJ-cgTNqetlW8h5k,41 +markupsafe/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/venv/lib/python3.12/site-packages/MarkupSafe-3.0.2.dist-info/WHEEL b/venv/lib/python3.12/site-packages/MarkupSafe-3.0.2.dist-info/WHEEL new file mode 100644 index 00000000..edd13a08 --- /dev/null +++ b/venv/lib/python3.12/site-packages/MarkupSafe-3.0.2.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (75.2.0) +Root-Is-Purelib: false +Tag: cp312-cp312-macosx_11_0_arm64 + diff --git a/venv/lib/python3.12/site-packages/MarkupSafe-3.0.2.dist-info/top_level.txt b/venv/lib/python3.12/site-packages/MarkupSafe-3.0.2.dist-info/top_level.txt new file mode 100644 index 00000000..75bf7292 --- /dev/null +++ b/venv/lib/python3.12/site-packages/MarkupSafe-3.0.2.dist-info/top_level.txt @@ -0,0 +1 @@ +markupsafe diff --git a/venv/lib/python3.12/site-packages/PyYAML-6.0.2.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/PyYAML-6.0.2.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/PyYAML-6.0.2.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.12/site-packages/PyYAML-6.0.2.dist-info/LICENSE b/venv/lib/python3.12/site-packages/PyYAML-6.0.2.dist-info/LICENSE new file mode 100644 index 00000000..2f1b8e15 --- /dev/null +++ b/venv/lib/python3.12/site-packages/PyYAML-6.0.2.dist-info/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2017-2021 Ingy döt Net +Copyright (c) 2006-2016 Kirill Simonov + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/venv/lib/python3.12/site-packages/PyYAML-6.0.2.dist-info/METADATA b/venv/lib/python3.12/site-packages/PyYAML-6.0.2.dist-info/METADATA new file mode 100644 index 00000000..db029b77 --- /dev/null +++ b/venv/lib/python3.12/site-packages/PyYAML-6.0.2.dist-info/METADATA @@ -0,0 +1,46 @@ +Metadata-Version: 2.1 +Name: PyYAML +Version: 6.0.2 +Summary: YAML parser and emitter for Python +Home-page: https://pyyaml.org/ +Download-URL: https://pypi.org/project/PyYAML/ +Author: Kirill Simonov +Author-email: xi@resolvent.net +License: MIT +Project-URL: Bug Tracker, https://github.com/yaml/pyyaml/issues +Project-URL: CI, https://github.com/yaml/pyyaml/actions +Project-URL: Documentation, https://pyyaml.org/wiki/PyYAMLDocumentation +Project-URL: Mailing lists, http://lists.sourceforge.net/lists/listinfo/yaml-core +Project-URL: Source Code, https://github.com/yaml/pyyaml +Platform: Any +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Cython +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: Text Processing :: Markup +Requires-Python: >=3.8 +License-File: LICENSE + +YAML is a data serialization format designed for human readability +and interaction with scripting languages. PyYAML is a YAML parser +and emitter for Python. + +PyYAML features a complete YAML 1.1 parser, Unicode support, pickle +support, capable extension API, and sensible error messages. PyYAML +supports standard YAML tags and provides Python-specific tags that +allow to represent an arbitrary Python object. + +PyYAML is applicable for a broad range of tasks from complex +configuration files to object serialization and persistence. diff --git a/venv/lib/python3.12/site-packages/PyYAML-6.0.2.dist-info/RECORD b/venv/lib/python3.12/site-packages/PyYAML-6.0.2.dist-info/RECORD new file mode 100644 index 00000000..595704ba --- /dev/null +++ b/venv/lib/python3.12/site-packages/PyYAML-6.0.2.dist-info/RECORD @@ -0,0 +1,43 @@ +PyYAML-6.0.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +PyYAML-6.0.2.dist-info/LICENSE,sha256=jTko-dxEkP1jVwfLiOsmvXZBAqcoKVQwfT5RZ6V36KQ,1101 +PyYAML-6.0.2.dist-info/METADATA,sha256=9-odFB5seu4pGPcEv7E8iyxNF51_uKnaNGjLAhz2lto,2060 +PyYAML-6.0.2.dist-info/RECORD,, +PyYAML-6.0.2.dist-info/WHEEL,sha256=h9jBNgvnuEaix45NgESHvfNcOPGGNEywrbP9Un7hZlk,110 +PyYAML-6.0.2.dist-info/top_level.txt,sha256=rpj0IVMTisAjh_1vG3Ccf9v5jpCQwAz6cD1IVU5ZdhQ,11 +_yaml/__init__.py,sha256=04Ae_5osxahpJHa3XBZUAf4wi6XX32gR8D6X6p64GEA,1402 +_yaml/__pycache__/__init__.cpython-312.pyc,, +yaml/__init__.py,sha256=N35S01HMesFTe0aRRMWkPj0Pa8IEbHpE9FK7cr5Bdtw,12311 +yaml/__pycache__/__init__.cpython-312.pyc,, +yaml/__pycache__/composer.cpython-312.pyc,, +yaml/__pycache__/constructor.cpython-312.pyc,, +yaml/__pycache__/cyaml.cpython-312.pyc,, +yaml/__pycache__/dumper.cpython-312.pyc,, +yaml/__pycache__/emitter.cpython-312.pyc,, +yaml/__pycache__/error.cpython-312.pyc,, +yaml/__pycache__/events.cpython-312.pyc,, +yaml/__pycache__/loader.cpython-312.pyc,, +yaml/__pycache__/nodes.cpython-312.pyc,, +yaml/__pycache__/parser.cpython-312.pyc,, +yaml/__pycache__/reader.cpython-312.pyc,, +yaml/__pycache__/representer.cpython-312.pyc,, +yaml/__pycache__/resolver.cpython-312.pyc,, +yaml/__pycache__/scanner.cpython-312.pyc,, +yaml/__pycache__/serializer.cpython-312.pyc,, +yaml/__pycache__/tokens.cpython-312.pyc,, +yaml/_yaml.cpython-312-darwin.so,sha256=kJhYpZpKSMBUaR-esOCOg7Ao1QLyNGl3tVdptwNUd6Y,375656 +yaml/composer.py,sha256=_Ko30Wr6eDWUeUpauUGT3Lcg9QPBnOPVlTnIMRGJ9FM,4883 +yaml/constructor.py,sha256=kNgkfaeLUkwQYY_Q6Ff1Tz2XVw_pG1xVE9Ak7z-viLA,28639 +yaml/cyaml.py,sha256=6ZrAG9fAYvdVe2FK_w0hmXoG7ZYsoYUwapG8CiC72H0,3851 +yaml/dumper.py,sha256=PLctZlYwZLp7XmeUdwRuv4nYOZ2UBnDIUy8-lKfLF-o,2837 +yaml/emitter.py,sha256=jghtaU7eFwg31bG0B7RZea_29Adi9CKmXq_QjgQpCkQ,43006 +yaml/error.py,sha256=Ah9z-toHJUbE9j-M8YpxgSRM5CgLCcwVzJgLLRF2Fxo,2533 +yaml/events.py,sha256=50_TksgQiE4up-lKo_V-nBy-tAIxkIPQxY5qDhKCeHw,2445 +yaml/loader.py,sha256=UVa-zIqmkFSCIYq_PgSGm4NSJttHY2Rf_zQ4_b1fHN0,2061 +yaml/nodes.py,sha256=gPKNj8pKCdh2d4gr3gIYINnPOaOxGhJAUiYhGRnPE84,1440 +yaml/parser.py,sha256=ilWp5vvgoHFGzvOZDItFoGjD6D42nhlZrZyjAwa0oJo,25495 +yaml/reader.py,sha256=0dmzirOiDG4Xo41RnuQS7K9rkY3xjHiVasfDMNTqCNw,6794 +yaml/representer.py,sha256=IuWP-cAW9sHKEnS0gCqSa894k1Bg4cgTxaDwIcbRQ-Y,14190 +yaml/resolver.py,sha256=9L-VYfm4mWHxUD1Vg4X7rjDRK_7VZd6b92wzq7Y2IKY,9004 +yaml/scanner.py,sha256=YEM3iLZSaQwXcQRg2l2R4MdT0zGP2F9eHkKGKnHyWQY,51279 +yaml/serializer.py,sha256=ChuFgmhU01hj4xgI8GaKv6vfM2Bujwa9i7d2FAHj7cA,4165 +yaml/tokens.py,sha256=lTQIzSVw8Mg9wv459-TjiOQe6wVziqaRlqX2_89rp54,2573 diff --git a/venv/lib/python3.12/site-packages/PyYAML-6.0.2.dist-info/WHEEL b/venv/lib/python3.12/site-packages/PyYAML-6.0.2.dist-info/WHEEL new file mode 100644 index 00000000..fff8a43e --- /dev/null +++ b/venv/lib/python3.12/site-packages/PyYAML-6.0.2.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.44.0) +Root-Is-Purelib: false +Tag: cp312-cp312-macosx_11_0_arm64 + diff --git a/venv/lib/python3.12/site-packages/PyYAML-6.0.2.dist-info/top_level.txt b/venv/lib/python3.12/site-packages/PyYAML-6.0.2.dist-info/top_level.txt new file mode 100644 index 00000000..e6475e91 --- /dev/null +++ b/venv/lib/python3.12/site-packages/PyYAML-6.0.2.dist-info/top_level.txt @@ -0,0 +1,2 @@ +_yaml +yaml diff --git a/venv/lib/python3.12/site-packages/__pycache__/ghp_import.cpython-312.pyc b/venv/lib/python3.12/site-packages/__pycache__/ghp_import.cpython-312.pyc new file mode 100644 index 00000000..52f8d4d5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/__pycache__/ghp_import.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/__pycache__/six.cpython-312.pyc b/venv/lib/python3.12/site-packages/__pycache__/six.cpython-312.pyc new file mode 100644 index 00000000..49079b29 Binary files /dev/null and b/venv/lib/python3.12/site-packages/__pycache__/six.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/__pycache__/yaml_env_tag.cpython-312.pyc b/venv/lib/python3.12/site-packages/__pycache__/yaml_env_tag.cpython-312.pyc new file mode 100644 index 00000000..9d43d7ff Binary files /dev/null and b/venv/lib/python3.12/site-packages/__pycache__/yaml_env_tag.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/_watchdog_fsevents.cpython-312-darwin.so b/venv/lib/python3.12/site-packages/_watchdog_fsevents.cpython-312-darwin.so new file mode 100755 index 00000000..baa45773 Binary files /dev/null and b/venv/lib/python3.12/site-packages/_watchdog_fsevents.cpython-312-darwin.so differ diff --git a/venv/lib/python3.12/site-packages/_yaml/__init__.py b/venv/lib/python3.12/site-packages/_yaml/__init__.py new file mode 100644 index 00000000..7baa8c4b --- /dev/null +++ b/venv/lib/python3.12/site-packages/_yaml/__init__.py @@ -0,0 +1,33 @@ +# This is a stub package designed to roughly emulate the _yaml +# extension module, which previously existed as a standalone module +# and has been moved into the `yaml` package namespace. +# It does not perfectly mimic its old counterpart, but should get +# close enough for anyone who's relying on it even when they shouldn't. +import yaml + +# in some circumstances, the yaml module we imoprted may be from a different version, so we need +# to tread carefully when poking at it here (it may not have the attributes we expect) +if not getattr(yaml, '__with_libyaml__', False): + from sys import version_info + + exc = ModuleNotFoundError if version_info >= (3, 6) else ImportError + raise exc("No module named '_yaml'") +else: + from yaml._yaml import * + import warnings + warnings.warn( + 'The _yaml extension module is now located at yaml._yaml' + ' and its location is subject to change. To use the' + ' LibYAML-based parser and emitter, import from `yaml`:' + ' `from yaml import CLoader as Loader, CDumper as Dumper`.', + DeprecationWarning + ) + del warnings + # Don't `del yaml` here because yaml is actually an existing + # namespace member of _yaml. + +__name__ = '_yaml' +# If the module is top-level (i.e. not a part of any specific package) +# then the attribute should be set to ''. +# https://docs.python.org/3.8/library/types.html +__package__ = '' diff --git a/venv/lib/python3.12/site-packages/_yaml/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/_yaml/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 00000000..90126ed3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/_yaml/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/babel-2.17.0.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/babel-2.17.0.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/babel-2.17.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.12/site-packages/babel-2.17.0.dist-info/LICENSE b/venv/lib/python3.12/site-packages/babel-2.17.0.dist-info/LICENSE new file mode 100644 index 00000000..6ddae98e --- /dev/null +++ b/venv/lib/python3.12/site-packages/babel-2.17.0.dist-info/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2013-2025 by the Babel Team, see AUTHORS for more information. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/lib/python3.12/site-packages/babel-2.17.0.dist-info/METADATA b/venv/lib/python3.12/site-packages/babel-2.17.0.dist-info/METADATA new file mode 100644 index 00000000..8cdc8462 --- /dev/null +++ b/venv/lib/python3.12/site-packages/babel-2.17.0.dist-info/METADATA @@ -0,0 +1,54 @@ +Metadata-Version: 2.2 +Name: babel +Version: 2.17.0 +Summary: Internationalization utilities +Home-page: https://babel.pocoo.org/ +Author: Armin Ronacher +Author-email: armin.ronacher@active-4.com +Maintainer: Aarni Koskela +Maintainer-email: akx@iki.fi +License: BSD-3-Clause +Project-URL: Source, https://github.com/python-babel/babel +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Requires-Python: >=3.8 +License-File: LICENSE +Requires-Dist: pytz>=2015.7; python_version < "3.9" +Provides-Extra: dev +Requires-Dist: tzdata; sys_platform == "win32" and extra == "dev" +Requires-Dist: backports.zoneinfo; python_version < "3.9" and extra == "dev" +Requires-Dist: freezegun~=1.0; extra == "dev" +Requires-Dist: jinja2>=3.0; extra == "dev" +Requires-Dist: pytest-cov; extra == "dev" +Requires-Dist: pytest>=6.0; extra == "dev" +Requires-Dist: pytz; extra == "dev" +Requires-Dist: setuptools; extra == "dev" +Dynamic: author +Dynamic: author-email +Dynamic: classifier +Dynamic: description +Dynamic: home-page +Dynamic: license +Dynamic: maintainer +Dynamic: maintainer-email +Dynamic: project-url +Dynamic: provides-extra +Dynamic: requires-dist +Dynamic: requires-python +Dynamic: summary + +A collection of tools for internationalizing Python applications. diff --git a/venv/lib/python3.12/site-packages/babel-2.17.0.dist-info/RECORD b/venv/lib/python3.12/site-packages/babel-2.17.0.dist-info/RECORD new file mode 100644 index 00000000..16f574a3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/babel-2.17.0.dist-info/RECORD @@ -0,0 +1,1132 @@ +../../../bin/pybabel,sha256=aePI19aUBwf8nIUm8LIl0sYZlYnk7iTDaNv2lqDYsjc,263 +babel-2.17.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +babel-2.17.0.dist-info/LICENSE,sha256=eAGoT-gRJ5yzUL20-xj5TDaxjvgT-mqsBDtsbTlITQ0,1531 +babel-2.17.0.dist-info/METADATA,sha256=xbADd4FX7x9g3Jol93V9dBRbALmMLlTVcQCDcl12nnw,2032 +babel-2.17.0.dist-info/RECORD,, +babel-2.17.0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91 +babel-2.17.0.dist-info/entry_points.txt,sha256=Y2Cr1P3E8Yt7kqzvVz4wnTvD1H3-BVD4FOkVqHIGBfc,750 +babel-2.17.0.dist-info/top_level.txt,sha256=mQO3vNkqlcYs_xRaL5EpRIy1IRjMp4N9_vdwmiemPXo,6 +babel/__init__.py,sha256=Ow3KHphrtw4WSFK-FzacGR8JVX5JQZ1YCdfJKD8UYS0,882 +babel/__pycache__/__init__.cpython-312.pyc,, +babel/__pycache__/core.cpython-312.pyc,, +babel/__pycache__/dates.cpython-312.pyc,, +babel/__pycache__/languages.cpython-312.pyc,, +babel/__pycache__/lists.cpython-312.pyc,, +babel/__pycache__/localedata.cpython-312.pyc,, +babel/__pycache__/numbers.cpython-312.pyc,, +babel/__pycache__/plural.cpython-312.pyc,, +babel/__pycache__/support.cpython-312.pyc,, +babel/__pycache__/units.cpython-312.pyc,, +babel/__pycache__/util.cpython-312.pyc,, +babel/core.py,sha256=QbuWyWBg4RJlYQ5Avg-ci71j4ZQ07VUGhhVxdI9y-Qo,45371 +babel/dates.py,sha256=fJCaifpved33Rf5T1thxxPG6d3MmPU99wnIO2RAA-mc,76378 +babel/global.dat,sha256=wRYHp_nSwZk6S1S0kn3g20zHAYm2vr7ThmvV7uqDqgM,459316 +babel/languages.py,sha256=2LDTV9WizAuZVam0vfw-Q-QKYCQpF5DxLbmVoat7TKI,2844 +babel/lists.py,sha256=Y4m1gScEDZlHAweDb-H5J6bEhlzTFmhKCWywS2h6V8M,4361 +babel/locale-data/LICENSE.unicode,sha256=tMCujvBPcFn5bOW74EZ_n-b22Bu-E1F3Ad_rlh-00LY,2033 +babel/locale-data/aa.dat,sha256=CI5kOVaXWX2CQoHT6AXhrOMwFPCiQyd7Wke2QBJ1UtE,2795 +babel/locale-data/aa_DJ.dat,sha256=VKjSgKVsftG91nj0-AQMYFuF_p4YAGfRkgAvycB2EYU,1112 +babel/locale-data/aa_ER.dat,sha256=6b6qROwoeOq0-batM1Z7Ji2gvGDogl3ODcftt1Jkm9g,637 +babel/locale-data/aa_ET.dat,sha256=piwFu94H7e-jPwd4ZaWiDc-j2nAH_GiC5UZ2IxMx7bs,635 +babel/locale-data/ab.dat,sha256=xeio1NLBNIAXcafnonWjO3w0zofwM-aKr0-RiaL7pRQ,95311 +babel/locale-data/ab_GE.dat,sha256=b5be2Uv1YtP-bkMasJMzoQPxJPNLFqifXnCiK8rCyLE,635 +babel/locale-data/af.dat,sha256=pO9_3BpJSQAJ3gZ_j6ZdUpGq3zqEv6CIZZ-CpJTXwr4,144732 +babel/locale-data/af_NA.dat,sha256=zif__TyNY44mSCYOCrrglsyr_H31JhBNPwSJftg8200,1450 +babel/locale-data/af_ZA.dat,sha256=6lT-mLg7LRtyAEhWhiF2xVtiPyzEa28U6hsFw_BMrZE,635 +babel/locale-data/agq.dat,sha256=JJbF6NFR9qwwQTrIyrLh04DFJniIqDuFsbmHDGu5ThM,16628 +babel/locale-data/agq_CM.dat,sha256=hYtg7pRw7J6TsYHvhlEEv7DqpyXoK3KS9DAqtYtgBX0,636 +babel/locale-data/ak.dat,sha256=L73y_ylhZy39SlEaAwA4jCqP_92qLvIlsagl-o2RlKE,116224 +babel/locale-data/ak_GH.dat,sha256=tM-OSpXWyBwZFfKPVStWBbFv3dwNj_7AqcBz4ONrOQs,616 +babel/locale-data/am.dat,sha256=TddfeRISFvzjTFpB-vH3gjSKs2r8iT3CYckrriibXZY,173260 +babel/locale-data/am_ET.dat,sha256=2VZDSh5bY9vLLuTqcKYG66p5OYlvPRPX58rpNTFWPok,635 +babel/locale-data/an.dat,sha256=a1gEztcqWoMLW3f5_jlDxcPsYY-ItHKVud7gjisrS4E,28050 +babel/locale-data/an_ES.dat,sha256=rQuq-zKj7-fCjY-5fGGoGJT1x60pAyCEAVEswXPnHrI,653 +babel/locale-data/ann.dat,sha256=fUenvOdPjqayLEiVDRNncK8SOBi9gAGAdxMuOATlsI0,737 +babel/locale-data/ann_NG.dat,sha256=0i2K6cv6eCurgFgwtiCr09hYwZCA1kHkQaKnDyCD86M,617 +babel/locale-data/apc.dat,sha256=G96W-ebLqQlTuPN3-VqSfAY-GF0K99c6uXHza5f4e2g,1564 +babel/locale-data/apc_SY.dat,sha256=tagFeQz8BA9Pc7RMqkce1ZalfKIBjqg0SR4NlpGQaaM,679 +babel/locale-data/ar.dat,sha256=yAijlkkaH-RgRQidvCyNBkpi9Z7OTC9cNfIbKo4n4gk,288582 +babel/locale-data/ar_001.dat,sha256=hRKDI3wzxO5T191MfSdCn_MZ6ckM2Rz_r__0JZqS00s,1707 +babel/locale-data/ar_AE.dat,sha256=hEbIYUoNE5mRLOVXhmkGsvT0NAcq-rxs51VIMMzG9LY,982 +babel/locale-data/ar_BH.dat,sha256=FoQ2c3E3TDqKcioarWBvLi_MllJmSd_56r0U45_gHWw,720 +babel/locale-data/ar_DJ.dat,sha256=qbNam_QAmhobsIUfJLCL9pe146SvcmNIE_L3cWpmkBs,698 +babel/locale-data/ar_DZ.dat,sha256=_Y-JLC5hincnxze7sGmEpb7N4JnBh7LdFdaVPcSMDvM,1263 +babel/locale-data/ar_EG.dat,sha256=qE3gLQ6BzLaBOPNJVgaBhPQiIL2kQ4AiD_jLl7o52IM,720 +babel/locale-data/ar_EH.dat,sha256=a0xJdGJYzGFzfvwxZVDP7yURlTm93FQSTQ5qp3XDMYo,658 +babel/locale-data/ar_ER.dat,sha256=l-WjJfxm7kP05-Hnr5PingHaKWJ46ScfW4KEBcwD5HM,679 +babel/locale-data/ar_IL.dat,sha256=O3UYAjf4BB3NdcSQTn-RzNjyj4kzTBhX3q7E8-9Aihw,1264 +babel/locale-data/ar_IQ.dat,sha256=7-DnoHqsoBDe-ZXe0rIcEotb-F_CFyChc73dwnXuUoE,1975 +babel/locale-data/ar_JO.dat,sha256=hT1z-Me5SOjul3BHFubcqlyEk9H57fwD4EFENddiVt8,1398 +babel/locale-data/ar_KM.dat,sha256=Zx5XtfTuRPiW6iViOjeal_Fq-18EA7GuERViMP1_mc4,1230 +babel/locale-data/ar_KW.dat,sha256=1wSXHTcgFKwhNciVS8IvGKvkzzqOIMBFykEJEQXdhEE,720 +babel/locale-data/ar_LB.dat,sha256=-SuXO6LdhXNi4f_PHNrlxl8iXX47perQ_oS19bwRGpI,1414 +babel/locale-data/ar_LY.dat,sha256=2OxSGD1gkiWPcaRHd1qQv9ifG82WEjGahKqhsoFXfnY,1249 +babel/locale-data/ar_MA.dat,sha256=hTOwsXC8tsJxBVlksxmAMlIkn4kK50juJa6TuAPFkp0,1559 +babel/locale-data/ar_MR.dat,sha256=Kq7uT5LxvYXnGWz-cVcI6yDNximu9hZIpoKydmVse78,1359 +babel/locale-data/ar_OM.dat,sha256=HVOqTfoH3JVTHJL-yDBiIafNfckU7CIcO_sIzXBffKw,720 +babel/locale-data/ar_PS.dat,sha256=F-HZRPcMe_tJ8r9V01h-7mQ0UKQxQmuWuXNSi94MVMM,1336 +babel/locale-data/ar_QA.dat,sha256=UKfLpyX856fNgeifdr22wXpDncirWJi9NxqfSnS-7fw,720 +babel/locale-data/ar_SA.dat,sha256=8WE8HwCwO-OT_G2JBSgmPWSzyHOdIA3zajXPbBZr2Tg,24787 +babel/locale-data/ar_SD.dat,sha256=f7aUKlrKmmC7ijTjVwn5r9m3mfZfEob1a7GHVCj4Qxk,720 +babel/locale-data/ar_SO.dat,sha256=4LD_gds2VsvxdFHpFo56CxTFenzNdvEkMofc4oTGBTU,677 +babel/locale-data/ar_SS.dat,sha256=T6eeWPeFejmBC7InKqg-6qmWoer1vjl6srHe_5ybkDU,700 +babel/locale-data/ar_SY.dat,sha256=u42uhje_zt806WlUMpp5sIdi1yjw3Yv7vYbl6aELsyc,1398 +babel/locale-data/ar_TD.dat,sha256=OG7NYtRrJiDeRt7HP-B4ta5ri-6fdSnYomppfy2A4ec,658 +babel/locale-data/ar_TN.dat,sha256=xO974NyO5T-pRs-5b55sfQFGIwU2Y4q3VQUeqtqWf80,1201 +babel/locale-data/ar_YE.dat,sha256=vu8wYJgzcJ1douKfKLx3-hAg1sjndD5_PTHWG_bzRL0,720 +babel/locale-data/arn.dat,sha256=JB-QfwCOVQbVrLRlq2LMxU6SHO-V4ZaU2_0W15-0Rfs,721 +babel/locale-data/arn_CL.dat,sha256=cf_JrdIAxFwbltVwtloNC9527Du7zaoBDM-_KmctSvE,636 +babel/locale-data/as.dat,sha256=xoTmhieoAVjw59h2-_pLp0wjfZPVvYMV6qDSJjLrjq8,209876 +babel/locale-data/as_IN.dat,sha256=bE-OZ0Y3d2-QtD23ztjluSsFk1_RFNYMFzKgBC3waw4,658 +babel/locale-data/asa.dat,sha256=zzPu7L8RV8SVEKqeRiGIoWLBCJkeS0acDh0pYq87qMI,15492 +babel/locale-data/asa_TZ.dat,sha256=U0GU10aP58LxCeqyjl69RQCZi_6tTQxm2vni6XVkoqY,617 +babel/locale-data/ast.dat,sha256=_Qeoc0gz1ag9MkmNIuv4ufVjk0fH5xtW4M7vp8ST4hY,168961 +babel/locale-data/ast_ES.dat,sha256=51kFxigV5COuiPiNisp-OBFYMith5rUgBmU48rGk7Hc,654 +babel/locale-data/az.dat,sha256=2EcwMc8qIqCW_F0Ic2XjFQthkCGyfLNzJzJZU99ZcaA,173802 +babel/locale-data/az_Arab.dat,sha256=IGBla73oKavlLHhtIW37idHxTkmQWZ-S8NE54b65d4k,7499 +babel/locale-data/az_Arab_IQ.dat,sha256=VsM0-KQPCP4MUw-2wMsBv-F467cr2Bup3jfxx8Fc3ZE,678 +babel/locale-data/az_Arab_IR.dat,sha256=ROb0gFTPwK5CcjpQMB7CJ662eld9sZj7IA6QMrvxgto,678 +babel/locale-data/az_Arab_TR.dat,sha256=EV6cNf3keBNREHkfF98GGZGOZz4L5dxf8pFbujInaQY,635 +babel/locale-data/az_Cyrl.dat,sha256=ru4QYee174toLQsGPJn-d6xe65FsLQ87vBV9AW0o4D8,35540 +babel/locale-data/az_Cyrl_AZ.dat,sha256=GRZjR-gp6JYeLNxoiAKNdv9bkTmS1bpYnRJ0c9YIGws,635 +babel/locale-data/az_Latn.dat,sha256=aK2ziytg5Ig6GYk9tVCq4BEyV6UYy9Ip7F1_i4Pq1jw,2258 +babel/locale-data/az_Latn_AZ.dat,sha256=GRZjR-gp6JYeLNxoiAKNdv9bkTmS1bpYnRJ0c9YIGws,635 +babel/locale-data/ba.dat,sha256=Ly1V4wBNM4KCDGtuy9oCug4fZzVNEFqXOI8XKhkWFOQ,732 +babel/locale-data/ba_RU.dat,sha256=8YcwUOx3h4BcSfUIumKJ4wcFyiSgBM74SqenVIQUKi8,653 +babel/locale-data/bal.dat,sha256=mYMTNzej_lac3GimNq9dYgLY3Q4RtXpEvUkQ-zfzVqw,12849 +babel/locale-data/bal_Arab.dat,sha256=25CVl5rF7FqKEeHzAmt0BccVI_ODqmMP86KqQ4i6PL8,934 +babel/locale-data/bal_Arab_PK.dat,sha256=sy4tpUtN1St5yFoTp8C6-vi2bFGPjSaG_zDbY16Q988,636 +babel/locale-data/bal_Latn.dat,sha256=X9sEvC7SU0VUFnapzFKUq0nvc1az1c9lHmrCPHPCg4U,116909 +babel/locale-data/bal_Latn_PK.dat,sha256=sy4tpUtN1St5yFoTp8C6-vi2bFGPjSaG_zDbY16Q988,636 +babel/locale-data/bas.dat,sha256=hts2-gooZheGxndoyiVM9NwFrAitthfb4IPr7TtYb2U,16673 +babel/locale-data/bas_CM.dat,sha256=WBq-_rOvkBnT5pBaCyq844LLSRReQzg2HM0u7GmhKKM,636 +babel/locale-data/be.dat,sha256=8L6Q0C4wjbLMC9I7ltFoY1prATuRd-8nn0udprYBI6A,272322 +babel/locale-data/be_BY.dat,sha256=yN4Rf2-HqinMscTfe4jKEYpsCEaddHce6K25ol595Ag,635 +babel/locale-data/be_TARASK.dat,sha256=93xDxO06nuAugpWjrnx6C5k8Fs26HIAolUcUPuoHhbg,104118 +babel/locale-data/bem.dat,sha256=zBh5HxY-hRgaQd95anmqMAPP0UVzSV7JdcuqtJ78t1c,5782 +babel/locale-data/bem_ZM.dat,sha256=nQvsLK0FOSlgf6wspQdbj00IveO62c2Mvxjzn4eHGSs,617 +babel/locale-data/bew.dat,sha256=mfKR23PSPyS0AHxbaSuMvufZZQlMICxDGuPv78MtDbU,122095 +babel/locale-data/bew_ID.dat,sha256=JWKbnUQc4ZsJYTcBFypK3gSyFRK6Kp8UFEqdytnPMnM,636 +babel/locale-data/bez.dat,sha256=_saESrmbxsbQnGLACykFIs-aLUC-N7IigpyPWGLBebI,16300 +babel/locale-data/bez_TZ.dat,sha256=OwcKelhUxQWu-8Jm9nf2vCay6-8LQvg-ueZSN_mrGz0,617 +babel/locale-data/bg.dat,sha256=ofd_Tv4onIP1cE1pVO-171ySR9JJXuHKzpWqc-vtLP8,227940 +babel/locale-data/bg_BG.dat,sha256=CERFyuC4Dw9BfAZapqaq69iKAp7rBXWNCkLWAL5tAhQ,653 +babel/locale-data/bgc.dat,sha256=gW0lkQNX_h1SATbqzIqe0twmGqqfigkbqo7gvBCG1AI,2493 +babel/locale-data/bgc_IN.dat,sha256=hO39nPiyUx1kfCdagpCQ8lzPLycjdfk59jFQgsd3Qec,659 +babel/locale-data/bgn.dat,sha256=8RKzmGYGYkn5IF7NHRgPtaI9ozwCeEKoUE4aXQpItKY,28964 +babel/locale-data/bgn_AE.dat,sha256=5jo7AWXMNdiiEf-RqzB5xRVj0R89NFdwq5RAIEAeeRE,636 +babel/locale-data/bgn_AF.dat,sha256=UXCb78n57adZBdKHEGnhbmeVYppzlSscVOWnQNxfJOQ,679 +babel/locale-data/bgn_IR.dat,sha256=iXxyPD5Z-ouDWFpOE2atoi6-HHGoD6PNaT9c-TH6x7o,679 +babel/locale-data/bgn_OM.dat,sha256=3O0CzFrjWxOQFdN6-lDhCxRngoXfadd_dwYoVyjK9jA,679 +babel/locale-data/bgn_PK.dat,sha256=gdVUcc-isHd9lf0S-AmFoPIMwDZ8ay6NY4o1udWI6Yo,636 +babel/locale-data/bho.dat,sha256=JQAEhmajFwUOKB7ACN1-ZPcoKcXJaq0-FhsFCVcvn_8,2875 +babel/locale-data/bho_IN.dat,sha256=UcL_aZq35dOHME0Fux9zyt9B6BOTxyR5UvaGWKLs1jE,659 +babel/locale-data/blo.dat,sha256=I0gfsGv80xf7do-oxzjkh2or934YxYgDMkM9RHf7UXk,168499 +babel/locale-data/blo_BJ.dat,sha256=LM79oUzVI4JtLYEUklfbQxoweFAslN9jS-Ex-dya0mE,617 +babel/locale-data/blt.dat,sha256=6G2E9osBy9OrG1iUKLD9cwcRk3IV7fT11iI8zFkLaeo,723 +babel/locale-data/blt_VN.dat,sha256=vpTQEJYssmuBUn_pYqx7ZtYONNNz02_oBYlDMdWqKAc,636 +babel/locale-data/bm.dat,sha256=K0dLlxoDfJb4QLwa_D0SXfrbD53Xed_EJ21843fY9hY,15798 +babel/locale-data/bm_ML.dat,sha256=PAGrUwc1S6q32DDHTu5LkePFXe_M_i_WxIY4oLuUnzw,616 +babel/locale-data/bm_Nkoo.dat,sha256=UiQ-oKSXYmbnUj9nNXi-9Afx2taBc5EiN-JM1huGOno,2805 +babel/locale-data/bm_Nkoo_ML.dat,sha256=PAGrUwc1S6q32DDHTu5LkePFXe_M_i_WxIY4oLuUnzw,616 +babel/locale-data/bn.dat,sha256=00cRKYrwyk5snhrekLj0TQBVzZlV_PjQ0aLlsApgwxw,219185 +babel/locale-data/bn_BD.dat,sha256=mVW90kQmhDcwzKmkmgNAt5iMq1pLWmNndNID9fspVjY,635 +babel/locale-data/bn_IN.dat,sha256=Y4d5tMv6rG2A1fTcfN7-cMeZ1iH8XuqdZgpEBUy_FTg,4035 +babel/locale-data/bo.dat,sha256=2eVqnOFPv04Ix-1FVPHlDQ9UVTQC1JtC7-5lQ89aX7A,20102 +babel/locale-data/bo_CN.dat,sha256=xOnyre81Z1IHnWV5mJH0x313h-ZWu84hIXcMuB50IT0,635 +babel/locale-data/bo_IN.dat,sha256=GvYCgtrOD5zYYsfMtVmkCv8-y6His8Lg7iuytEGjnMI,1307 +babel/locale-data/br.dat,sha256=rBs1Bp9H6Q4ECO2mouoJmnCW0ux5CLPx1zy32GvpkCo,272612 +babel/locale-data/br_FR.dat,sha256=3oVDdZd7Xf4Q-hwmYraF42SM_3NtvSoiZlmlh6gSNOs,653 +babel/locale-data/brx.dat,sha256=0L8rfQwjwtj3y5M5zzdEB10way4I9P2m8A2E0mTIcac,160164 +babel/locale-data/brx_IN.dat,sha256=NKuJh0cEhRQasy6JAGUi5HbqdnhhEFTQ_7osgtdG5Iw,659 +babel/locale-data/bs.dat,sha256=YVLbXGLHf6QZ9vYnCjxjO3uBImuYnoxR-cEuBPVbP_w,213968 +babel/locale-data/bs_Cyrl.dat,sha256=oqFwEFFfYqyzkVvswj8CR2B_RCb51Myv8jQaDDjz0u8,219235 +babel/locale-data/bs_Cyrl_BA.dat,sha256=oA7rsQckE_IxCpXXB3F3KWQwM3WZTR7WAdYxJN98oeM,635 +babel/locale-data/bs_Latn.dat,sha256=6L9SY85hONRHO4Jz389C2Ziy7llKcDoyVNiE96ozCJ8,1990 +babel/locale-data/bs_Latn_BA.dat,sha256=oA7rsQckE_IxCpXXB3F3KWQwM3WZTR7WAdYxJN98oeM,635 +babel/locale-data/bss.dat,sha256=n9LaBwBIJ7adjJ63G7Q9o00llhPJtkgmeEp08nAa4D4,978 +babel/locale-data/bss_CM.dat,sha256=TD7ixCHREfOLDsDJn8FY5YqGYH9czdEzdULCIl_0GhM,636 +babel/locale-data/byn.dat,sha256=qT32E4jh6H_qm7HYSsAbKmaKluaLrGNXNl3F3FivncE,13403 +babel/locale-data/byn_ER.dat,sha256=oAaZEqMJuTbMITO-ZO6gg8qjwROukzh5VgjtLp6rqD4,617 +babel/locale-data/ca.dat,sha256=CIT0-7X7_e8RKWRrhLSmG-aBC2D51-hvRrTV-7iBav8,184038 +babel/locale-data/ca_AD.dat,sha256=tsLumbhbbeXw627WKi-sQdSTFwNpcSRvJIEYcrOMqOM,653 +babel/locale-data/ca_ES.dat,sha256=T-zv3FfnCJA2WUQTeBADKV82ghY-r60wk8mXtD9wbnI,653 +babel/locale-data/ca_ES_VALENCIA.dat,sha256=7Q59_PX0VWyLsDkC-qscZ-dKJ9AFzz7kCzJz4fTdBWY,2976 +babel/locale-data/ca_FR.dat,sha256=5HUFYUl5QRaUMwJxGLIqUhDO4_wF_crrr346wJmEOZo,672 +babel/locale-data/ca_IT.dat,sha256=Zh2rtOq8MIDC2CQXWnhp5HOvPfXNxhRGnnZViOgc1Mg,653 +babel/locale-data/cad.dat,sha256=XxlMk1b9CWE3wERhhQQN0wxW7iq8Mrngb05RXenmaSI,2476 +babel/locale-data/cad_US.dat,sha256=FWV1sofgr9no5P9yBxNDLeKWoQd80XEYEilmLwamkSc,654 +babel/locale-data/cch.dat,sha256=J4S73psT7C_cXs1VNNYow47plBodQ4PKINC59kzvfAw,2430 +babel/locale-data/cch_NG.dat,sha256=0adhw9xNMlVEqDVKvHe4HCPtbjcw5NSo4ny_pOjl0-4,617 +babel/locale-data/ccp.dat,sha256=Jn_O5MwN6DMGxNrXUpdrhT_z5ZOYh3-Sm5yGUcV8bII,207292 +babel/locale-data/ccp_BD.dat,sha256=f00776fnI484B-mzW0rY22KNkzV6wtmk7uxrSK_x9bU,636 +babel/locale-data/ccp_IN.dat,sha256=jMJimrQrx-b96hBSLi8sOyHtYRwNZryt-he0rhwEZ2s,659 +babel/locale-data/ce.dat,sha256=yuopW5QCq1u8E1Rs0hEOv_y1uvbem_P3lK-_AvU5zLE,135403 +babel/locale-data/ce_RU.dat,sha256=NgNmp1uFnm2MT-s_yx_V8KuW1dSR6_SEWf4NcqrAL9o,653 +babel/locale-data/ceb.dat,sha256=fwgJPAV6TfvyavZUW-9pKeLUacMuH03hCkSkHdIGm8I,100779 +babel/locale-data/ceb_PH.dat,sha256=Z2zXPyNI3Yu3QcGL6lT1nIsl04vDDLFvyd9GeucDtqA,636 +babel/locale-data/cgg.dat,sha256=vgLxp4dDUjKyHy-36IvfaPEy6IiXbDiMKShuEKJuXHU,15621 +babel/locale-data/cgg_UG.dat,sha256=HZBma4MFuX2Q7sk-SOYk5OUkbMXWWr_tEInOqPboNYs,640 +babel/locale-data/cho.dat,sha256=hWEL5XrIWAnqjYSb78iPaj2xh34KDcPYAMc46S7ZLuU,795 +babel/locale-data/cho_US.dat,sha256=T3CacgkE-uKPi0CV7fbcCLTNfdjEco_A50vYciuXGGI,654 +babel/locale-data/chr.dat,sha256=4a6eJjX1JZ-inCk-J8gHJq1QtoK3FpOXhOlQMDbHjwM,187187 +babel/locale-data/chr_US.dat,sha256=wZE2RrJ-5jjUQa1RCJgL7hkXdsU3t91orD-ULDYMXSM,654 +babel/locale-data/cic.dat,sha256=KgrmFl1fudc1tdR_g39YkGR6bcIedQMFnUjh831VIhM,2732 +babel/locale-data/cic_US.dat,sha256=DvW1XJS1hEp_W_5ol0pIECeUb3RqXsyieKVS8J6caJ4,654 +babel/locale-data/ckb.dat,sha256=s6wh2TRjabPo4G64RMoQD_J7FK77RBwsK8R0TH1xytI,35553 +babel/locale-data/ckb_IQ.dat,sha256=Osgu1RAUf2yQpZ-nNBKMxTFJnH32ItLg0kVVzjNfmeo,679 +babel/locale-data/ckb_IR.dat,sha256=6wWzlZWn7oDmGIdnF7UtCfIcUgQ1zvMxtAcEw4bbM-g,1231 +babel/locale-data/co.dat,sha256=NbKZ99wTpG7Y64LnyxTQJIo5SYQaUAOybc68DN6IbUY,12637 +babel/locale-data/co_FR.dat,sha256=DAmwXn4n1mRSsT17-DcU0vdVSI_JfVR8oGOnj7hTFHk,653 +babel/locale-data/cs.dat,sha256=o7qnhuIqxpPIfD-oyVLsvpmhtRiIOzgJBoMaZvyvPL4,247800 +babel/locale-data/cs_CZ.dat,sha256=keaiqSsGtB_axj8FvdYDOCDmlg2PbQMI6qnW-2mFUTY,653 +babel/locale-data/csw.dat,sha256=a31wJpIkcegvNW9_O8LfIY4-HK0zpLn4MBUEBb9C3PQ,20303 +babel/locale-data/csw_CA.dat,sha256=aWccZHGNU7G4JJdlGRnqyqm0ROubX0rwI9SnH_EhAxU,636 +babel/locale-data/cu.dat,sha256=mlGQkibxBcukKPAJawHruBMuw21x1j42ebTnraxW55w,16865 +babel/locale-data/cu_RU.dat,sha256=GcbqgE8Mlglk5EGGSVcCR-Zb8AmCqHGE8cbmAUSbpdY,653 +babel/locale-data/cv.dat,sha256=BUqS-4RvAuycOlAm_k33eV98VCp5bIUmrWUqjhmsqls,198348 +babel/locale-data/cv_RU.dat,sha256=oVPRtcHzmRmkAy26Bv1jLiWmm0P18ruvHlCQ5F3IFY4,653 +babel/locale-data/cy.dat,sha256=-bN00WqRhdnCL9VdnGgtUtwbmZ7n2r96oZdLuxSP8KM,276313 +babel/locale-data/cy_GB.dat,sha256=dyPULIteKk9gP7lGQiDtsiH5QDqNdlxMdK__2dq90dg,653 +babel/locale-data/da.dat,sha256=VUVxNljiubvj3e_PhimeMLBVZlAt1GT2bAfnyYu671E,171756 +babel/locale-data/da_DK.dat,sha256=qNTw1H8WXsV8qGBHxu6noCUOV3BcoR63ERJF8Xc86Ls,653 +babel/locale-data/da_GL.dat,sha256=9BNeKX1-U4NDr3pBU1ZnCGAasdYZjBMsvpdS5uTf9qs,616 +babel/locale-data/dav.dat,sha256=LBf1uH_Q7632sBVhZgQSrdn5LU2pyIU389h4LircJM4,15690 +babel/locale-data/dav_KE.dat,sha256=MfFFEDT6fib5be-EmziiEF0NAnEV6lXD7svYtHUtmn0,636 +babel/locale-data/de.dat,sha256=l6ABVg2zXPX9Ws9g2-Weve05S46ii0hnbuCViS6mtmA,178630 +babel/locale-data/de_AT.dat,sha256=OhoRvklFcyYUJXplBoK2rAITKRi6r81lwOaixIuffcA,2010 +babel/locale-data/de_BE.dat,sha256=wvdWykhix9Slofcjm2tx2aEhMjffmMLsNrd8709NIR0,653 +babel/locale-data/de_CH.dat,sha256=PH2rDxHA3lpTBkPMCwWjapEDJtgrkHhq2_pAdSKSiXE,4091 +babel/locale-data/de_DE.dat,sha256=SM44YjGlqgh7bKlXvUdt53t9hnCX5o46ujI4IrGPpeI,653 +babel/locale-data/de_IT.dat,sha256=ALPY2QGlR9-kphEk4KggHVS51fQwEDVRd7aHLIiP9fw,1448 +babel/locale-data/de_LI.dat,sha256=FnGRiippDTTFRFswU_4PblQruAuWuPEpa8cGbXPJpbE,1413 +babel/locale-data/de_LU.dat,sha256=QAkvLfyKmZdQVfkk2BbE2AMisK1i7yhRetcApqn4dxA,1067 +babel/locale-data/dje.dat,sha256=6NxQgBas9S5kmjbRcbcuuIIxKaI5_MlfmSUltxJuraY,15530 +babel/locale-data/dje_NE.dat,sha256=lU1vag_HJ4OrbYNYWFXuSdtqLwSlwBbvD6Tg-hl36e4,617 +babel/locale-data/doi.dat,sha256=JrHDjsVlGntLL6qYf9NH52BWAGCSGeGVaHg_rgUiGkM,47442 +babel/locale-data/doi_IN.dat,sha256=-q_oAqvdTizFG1DB_1p8sZVyjbaFFQ_FFPtNCyVn9ZA,659 +babel/locale-data/dsb.dat,sha256=KvZxymKZ5ZbxQ-4gS_uvnNkrYMYn3D-BKz0MGAn5488,203926 +babel/locale-data/dsb_DE.dat,sha256=Z2Dzjx7eXscEBEAb-ymvb1GKP4GVNCt5L4i-BYC_vrE,654 +babel/locale-data/dua.dat,sha256=lmO8ssPF9bIgTU2SM_SzrGWodmsGsmbS485uZNDasq4,4827 +babel/locale-data/dua_CM.dat,sha256=Z3vD8WEr2Wlqqt39anvKXVhz4_yBSm4t5BoAaEz9PgE,636 +babel/locale-data/dv.dat,sha256=-1zCv4GXiRHdWjQk0qXWNPSf4FX4Zp-Ij2jwgz5BsGk,2225 +babel/locale-data/dv_MV.dat,sha256=lvuU5gj3vDykU2ftU1_4Lza-130DB6_ZwylDrdb_GlY,635 +babel/locale-data/dyo.dat,sha256=w71HDmFfCuKN9nUJnuqpHR9DDAmPLkwIu9E0abhYjWM,9854 +babel/locale-data/dyo_SN.dat,sha256=Z6N-NIVcKkx6sFUm8LfN74usyZfgeI6swJL_A_XbBcc,617 +babel/locale-data/dz.dat,sha256=dbPhCcTpoQh6szaBA4TN5eCi3sbG1UBvlQQxtMGHLEU,85026 +babel/locale-data/dz_BT.dat,sha256=KWd6cypWse4XfXbY4BmTlxGjlnRksYNcQYi_iyXluKw,635 +babel/locale-data/ebu.dat,sha256=S8bggVknCbVHw5x9aJQGB9kUBFNcwj9EONHRNtbaWCA,15583 +babel/locale-data/ebu_KE.dat,sha256=GEcGqVmH0WwgP1BCsd9vnSGxGXl8ON4UPTRbyeaNDfw,636 +babel/locale-data/ee.dat,sha256=xJ5vpKJa546rTvKKQI75-HvYmu6VjEedXENlPSeDgCg,88730 +babel/locale-data/ee_GH.dat,sha256=SkQi-aVmtqi3S99DUTLQzZqUE4LrAIWuy25QuZqYAfI,616 +babel/locale-data/ee_TG.dat,sha256=7y8-rDvag7_EQtEObq3fpHaRedVmuEREnQwjktmLwgk,1168 +babel/locale-data/el.dat,sha256=pMPkYRvK3y4IF93Z-127ZF_-_P_QV-_460LgZAHCw9o,247970 +babel/locale-data/el_CY.dat,sha256=POPjzTYMuP7Ijf1S88cAFR0ge9Id_sSRNembKdhk2aE,635 +babel/locale-data/el_GR.dat,sha256=S32Cm-1ENgLL_lDecCDI4qDfyd3wlQ1JbUwOnW1whm8,653 +babel/locale-data/el_POLYTON.dat,sha256=lkcs2uXW6mWXztmsAXnapnSuQ6gkns1WwsVgrg-24Uo,14964 +babel/locale-data/en.dat,sha256=C0EMuCmMqd7xbacUUkDzM6xvzhEWWQgbd0sIPvX5QbA,217633 +babel/locale-data/en_001.dat,sha256=X7NFTy08b5AyR9I6BDy79hP3x_nq018WoQmMSzR5S1U,31614 +babel/locale-data/en_150.dat,sha256=cjKCV1ZLhNY4TvqJF8BXvQR1jVUBFrdjL1RKSnBa0vc,1851 +babel/locale-data/en_AE.dat,sha256=T2jBGVJpPTUgTICdaQnAVjSsYCVkQLfjabyStaPl2CU,4164 +babel/locale-data/en_AG.dat,sha256=POWSM98pRq9k7lqCsxr-MS9PA-zLoOVUKUcI9iXtKq4,654 +babel/locale-data/en_AI.dat,sha256=7iwWKftqPdOdWJ7v94jsre2ZIq2cDpbVGAs_ScBztbs,1206 +babel/locale-data/en_AS.dat,sha256=M-N7v6BuP47wkTFmv5by1w42IQCUiSqKbaVTXtnwF8Q,635 +babel/locale-data/en_AT.dat,sha256=05-bVy1aMFWZlHMBC86A7OVy1zlhJw7hsjyvIOjxE70,1309 +babel/locale-data/en_AU.dat,sha256=kEhWopkI0vkAChxsUmnaAX4htgJ36t9hiVtaS6t5Gvk,20023 +babel/locale-data/en_BB.dat,sha256=J8vppKlBp03jJvVuVp6Z4GMc_N0BukeQ8f8q14y8fwU,635 +babel/locale-data/en_BE.dat,sha256=0JXHgqUIWZYe88FYC97spm2TqyIaNOwDJBt3LBmUF7A,1547 +babel/locale-data/en_BI.dat,sha256=AbdI5FjEqQLOMbBtD3wLW_7AccuOwY9MZUFpMF1uEco,1189 +babel/locale-data/en_BM.dat,sha256=cZE8sxf44ETY6nCxOgbvJN2RhABZrRaP8SjwsbSepuE,654 +babel/locale-data/en_BS.dat,sha256=sGvlF2LPwG5eVA3IuH8-4S5X7BrvQ5LcgZ8gfAkkwwc,752 +babel/locale-data/en_BW.dat,sha256=k5bc9gYBdl_8ZyCelrlS9ZenMzs8cdfl2MxeCnOAr88,2850 +babel/locale-data/en_BZ.dat,sha256=LJsn2M0RWMbxDmGRxs2lk2ZvkgoguwPf7oLBYQydU4A,2979 +babel/locale-data/en_CA.dat,sha256=za7pG-TGpows2k_XJL8dpzLVs1oOSMKN3zwyG79OAqI,37924 +babel/locale-data/en_CC.dat,sha256=34a3Vb57hT2reRvwn0Ud1nXkoYQJoPmaqh1n5qflFdI,1187 +babel/locale-data/en_CH.dat,sha256=Nfkwk2HNOccSH8Co5SWYnFdHWEDEpKNPQ2o9a67vt_k,1866 +babel/locale-data/en_CK.dat,sha256=gvU21BSAugpjE7CT3xnpHML6oRgHPaBoq7W11YbG-1U,1187 +babel/locale-data/en_CM.dat,sha256=r_7Yzep6S0ZOFvk_TORm5Qhi55yjehVQAotaLnn7igQ,1437 +babel/locale-data/en_CX.dat,sha256=lKYbIRS6r-WMUd1ewX7Q4TpX0PBVcsYN7Cs263_xosM,1187 +babel/locale-data/en_CY.dat,sha256=kQZfhQdPfdBeePOngn-4CpA3ZJ7Th-lmZ-jOrBe115o,635 +babel/locale-data/en_DE.dat,sha256=oI3TmMDoQZsGddvDb0fbbwd_LdTeFbFPdFkZhst2q7Y,1027 +babel/locale-data/en_DG.dat,sha256=xtjjxkuFB7I4p3n0f4Evz631RtMQhIzpoYwUzOzICqA,1168 +babel/locale-data/en_DK.dat,sha256=pE7x4MDJvZGFnqQqXMCDWoMV3Ovu2qH-QO2oYNvgL4Y,2425 +babel/locale-data/en_DM.dat,sha256=gbriVhXcdXi5GWmpgWdGB1LUwI2yquuLk1khkzpo1UU,654 +babel/locale-data/en_Dsrt.dat,sha256=Ttd13uhdXKMHpNCWaH5q74JJ3fok1gV4MFIGd1fGxRk,38656 +babel/locale-data/en_Dsrt_US.dat,sha256=p8c_L5tEvzYmacz0tmq58Wsbfp9wMV_PBgz5R_v7I-k,653 +babel/locale-data/en_ER.dat,sha256=U1vYaQVwnVgfmO9l2_GjBnAhHrShbiHU_E-hAclDvck,887 +babel/locale-data/en_FI.dat,sha256=i4gKdljmc9cXO-E1IT6JGmq2JsRRH71sKe3yUmkhIa8,2357 +babel/locale-data/en_FJ.dat,sha256=k-hXekogcQzt00qXdcWODsHbg7Vh9U8Vx84bERd8CHM,672 +babel/locale-data/en_FK.dat,sha256=r6OGl7mX4ep5aeFMlhog6Xr--kDKFEVhDcepSJBYZWw,1210 +babel/locale-data/en_FM.dat,sha256=88mCYM5_VDSYpJ_mV4AStjiDpKAo_1W7GbmKcI9f_sk,616 +babel/locale-data/en_GB.dat,sha256=2qd2bKhxJeiwIneKwRmH89UCmnYfBdpOFvL02en-YKU,3440 +babel/locale-data/en_GD.dat,sha256=c2ap41e5x-OY5dEYZ5wD4ByRbEzlx0845R1lPBLnJus,635 +babel/locale-data/en_GG.dat,sha256=qhfvbRdldEA4467X0_8N09HwT1ZRPJJtb7Evpvbbarg,1273 +babel/locale-data/en_GH.dat,sha256=bBpzsHl9QhNAQ7hrUtKb6TwGvZYibXQwXfuJVgLMlZ0,889 +babel/locale-data/en_GI.dat,sha256=veSOVDagnkTwyqxP4u5mOkRG8_W4MCZ5ptMBoGOg9SE,1228 +babel/locale-data/en_GM.dat,sha256=jfHQeiMTDNgnIpx3WZ9hOpCR_LXCYLLrVN4SUakudi8,885 +babel/locale-data/en_GU.dat,sha256=WDR_uybAkUii6RHT2x62pzCLvX4bMstibEztBvLSuxI,715 +babel/locale-data/en_GY.dat,sha256=TDiVa4YSapsK9rjQN3ATnrj3m267l35pqRLI7r5bLFc,694 +babel/locale-data/en_HK.dat,sha256=meLPc5_3zahna_xycXEf75TrW5_4BHuqMQcMyoo1eYQ,2315 +babel/locale-data/en_ID.dat,sha256=b8R7owdV-fl30WrUhFz-1YGHiFweaoYs_bBFr9pGhD0,3172 +babel/locale-data/en_IE.dat,sha256=ZNe5XFbdM4UnYu6KaGYHggQqzIVCWwWof5C5ktTsHn4,2094 +babel/locale-data/en_IL.dat,sha256=O4-JPTBiiLezF-iUSDu73id-0VWlSiE0ie8z9qRaxq8,1424 +babel/locale-data/en_IM.dat,sha256=u3JX9jrrsW_G5RQXHsQIGl6ikVduckeldeS_FjKG5Y0,1273 +babel/locale-data/en_IN.dat,sha256=2d4aWIy6Q72ExhZHnku5HKbKmSSavvB7zkflqlBsRQA,14809 +babel/locale-data/en_IO.dat,sha256=TOM0TxdcWcu1wyBXJxsrE-uCIZsbYTCmUkTdfCaOO-E,1168 +babel/locale-data/en_JE.dat,sha256=IxL3Ry70pMOXDbCwU6LZllHXZmg8XHbjocavEWvb9jY,1273 +babel/locale-data/en_JM.dat,sha256=Z9GFZ6aFGEvIEzlbOlA5bBBvxgcxXx6s_7AxPMp2Yg4,1666 +babel/locale-data/en_KE.dat,sha256=oJa3lUdjavkdhkMK_bcnJ08waK9lfIRs8mFkG0JYf-4,1458 +babel/locale-data/en_KI.dat,sha256=3yfkrwv7Yd9yxDNnXz9Sv8ghbDvqczZTAELr3uGNYn0,635 +babel/locale-data/en_KN.dat,sha256=cFebccXOrQo2X1zmHoHYB2vzXynoroYImJTMnK9j3h0,635 +babel/locale-data/en_KY.dat,sha256=slmSWk0IEz_UK7PfHX4KyHlf_iBRfuoOWVuEB0aKaYI,733 +babel/locale-data/en_LC.dat,sha256=jUYdKd248Jkj5XVxXXbvXnfxu4BH_3s4Ld-O67qbPkE,635 +babel/locale-data/en_LR.dat,sha256=ribT8_azFjDC8uzIsOwkGTjfgEiFlSG_wAb_wG1Fmnw,885 +babel/locale-data/en_LS.dat,sha256=2HtV3gUrynDl2mVvqywMfff8VnVjz2ryoNS7D2n5T9c,885 +babel/locale-data/en_MG.dat,sha256=dtMrsBqOpwQExs7dVukbUzWojz_6swx5Ng6OXdKxsSs,1438 +babel/locale-data/en_MH.dat,sha256=NZjQknLyokLgJRNPicAZ55_VoRCATz_WvAxMo_I-HOg,1368 +babel/locale-data/en_MO.dat,sha256=UJVR7td-vlRY_a36ZWbxr_PcK6egIUQSKoky3DeAmEA,830 +babel/locale-data/en_MP.dat,sha256=Sd2vA3CdR_GBw1vQQQBfKIY0asJGsVUwZ6gD-h7Or8o,1349 +babel/locale-data/en_MS.dat,sha256=BXrA3odGnWSeR6404L85ErqMKxTnRCZ16UWL2RpD7TY,1187 +babel/locale-data/en_MT.dat,sha256=sODJaESHndxdT9SYZcnESDA6G6oln6n9NCojeHhKvJg,1990 +babel/locale-data/en_MU.dat,sha256=SefoZho0XzSzzU_Y_-udWPCuRgz8ujdUVkL7hhzZO8Q,1438 +babel/locale-data/en_MV.dat,sha256=Spp5zsOCHzCeZOyUdYqXoqmSoIJ0pxRR3b3nI27jvTs,2034 +babel/locale-data/en_MW.dat,sha256=5d20Ih8j6iwBY545I4nObBWdv9FL37nE-ggfpGeXSVs,886 +babel/locale-data/en_MY.dat,sha256=nUwwM17W9W78nnbXGmrx2zComZQ9Fg_pu3va9wPZzvY,716 +babel/locale-data/en_NA.dat,sha256=-yNAAmx38RoVJt3YRH7Tp1xO9o3GsSVDU6uF_GKhGb0,885 +babel/locale-data/en_NF.dat,sha256=6LqhwHyOnkQA4TNvseuiE1a5Ov7DV3y46CZ0ENcibXE,1187 +babel/locale-data/en_NG.dat,sha256=dvbDZYdlkmhF-IU8M6s2p_kHswy8HhdmR3LLc4EhePI,1439 +babel/locale-data/en_NL.dat,sha256=dDCtXReh9uMfUOVqGWaWjfV1-dLe5_yj-o_HrKZGMeM,1192 +babel/locale-data/en_NR.dat,sha256=ZiUNBY0c1T3LQP-qGmtuxLW2Jfc-lcVTuWpB2jxfAGI,1187 +babel/locale-data/en_NU.dat,sha256=jU4HXz_3l5k-luPPsjZmZBsdOVB5ZfkN1Xuk5i2pduc,1187 +babel/locale-data/en_NZ.dat,sha256=z_qul-H3zQn0Q_lrrHceySj1m2TQaaJZd3QMXE31CcA,2340 +babel/locale-data/en_PG.dat,sha256=WV6D2yT-4GX6ftbnUkK-ihfNg8Y-ImX1Af56okx8LTA,635 +babel/locale-data/en_PH.dat,sha256=oPz_wDvxfCHBpkBUaZ0RiwtPrLtx-9IigR0TIK1b-IM,635 +babel/locale-data/en_PK.dat,sha256=KS_KXItkhkUULbKd9mHdWD9ok4WjZbVSXhU8e5u8blo,2074 +babel/locale-data/en_PN.dat,sha256=qpEWWX0fvhzV6gcX_SHNbJW7E-5FikKwXrQAP9BWmXc,1187 +babel/locale-data/en_PR.dat,sha256=bpAu9yoeKxzQTF5Be7AfXtyE3wax0eP0m-Eu-Cr0jKE,635 +babel/locale-data/en_PW.dat,sha256=--3_iiNqupR9R3ccq5foj9HzkADBEwBYdKE_AxLZD3w,714 +babel/locale-data/en_RW.dat,sha256=Et3hOZwaSDECrCzypgYq1QxKxxnB3XYziIeEe_WiY7c,1438 +babel/locale-data/en_SB.dat,sha256=h9TJ69YThzzLGTh49BBEmRBxSiZhZpQrVJif0QglLVs,635 +babel/locale-data/en_SC.dat,sha256=omYtCZBrLl8fHuT6XInezdetV7WqWZVYriJRnlWKbTg,1188 +babel/locale-data/en_SD.dat,sha256=ozd3rV_icr8p2H9V6StOSMkoHBPNBsXsay1GMRWZ1zs,928 +babel/locale-data/en_SE.dat,sha256=96guhEOzGr2Lcbd3xZ4j7HTj-0H0oaGMQIbI9a_5g48,1502 +babel/locale-data/en_SG.dat,sha256=u9U5CLnqs0iJmSnMsPBHF71HQLpFM3siADzZeC7iaJ0,2096 +babel/locale-data/en_SH.dat,sha256=rBm_0zxjHnEVzs_CPCyuz5LpI9isA2S6copUuNbxCcM,1210 +babel/locale-data/en_SI.dat,sha256=kkeEkPnW48iNoiwNZb0KV29DPU0oVNuENTi_Wmp2ALc,1046 +babel/locale-data/en_SL.dat,sha256=4hJLAAnyuEVVQZ13NC7wZ0JYJQ3rvX0SsOAp0OoW7_U,886 +babel/locale-data/en_SS.dat,sha256=C4kpyEFjyKT9-6wt2pGl4125aN0Bp0wpcYBrr62nPOc,908 +babel/locale-data/en_SX.dat,sha256=7vESruRIiaBqWxuSRpIE2U6PgWwJ9mxgbjjH_XAraa0,1190 +babel/locale-data/en_SZ.dat,sha256=A9wkzZ5cFffcEf1FobYRPxR1EAFwqQvqELsqRpNleOg,885 +babel/locale-data/en_Shaw.dat,sha256=5NskTfYsGHZ63w2s3eYrzrogPpVZeqY3wd60hViYIbo,4774 +babel/locale-data/en_Shaw_GB.dat,sha256=uAIWlJ5gr_NR6lncmMaBrjXUPhZ33rGsXoHz8hrd_E0,653 +babel/locale-data/en_TC.dat,sha256=UWqEc9zpN3NdS1dymbUv2iGKhLkH65Kk9uNFxdXGnOw,616 +babel/locale-data/en_TK.dat,sha256=qz4RHtHQ6cNjB8xW8_BIHdzW1cEcQuyJfdqFIybXAs0,1187 +babel/locale-data/en_TO.dat,sha256=OehJQf8kvWi7A-Rwo2hPjs1Z9pTdg7_Hyx_ewWxbLQ8,636 +babel/locale-data/en_TT.dat,sha256=L23iDn_zvh16iPE5uBlLCRvECSozqyzU9W2OcSjYogE,654 +babel/locale-data/en_TV.dat,sha256=3sj3NCjQg-9UOdTz7wDJRpjx57bI0bntpmx0T50OK9E,1187 +babel/locale-data/en_TZ.dat,sha256=5Q87mjSEaCMmWeWwtesb9lJw2tIWOsvyScG1CqSpWvs,1439 +babel/locale-data/en_UG.dat,sha256=aQLfdbfozsYhK3EG9JXVxsmT4B7UvfSzNBo7kO2WSsc,1462 +babel/locale-data/en_UM.dat,sha256=qd26Sl5bJTEgHU25hvskp4LMxvRJByLOSvUikqz0J4I,653 +babel/locale-data/en_US.dat,sha256=p8c_L5tEvzYmacz0tmq58Wsbfp9wMV_PBgz5R_v7I-k,653 +babel/locale-data/en_US_POSIX.dat,sha256=U8r77o6qQID3Sd5FiZqukSpQnq4LV1BDwHUeuNZjW10,1321 +babel/locale-data/en_VC.dat,sha256=ES4c4Xt6OvJDhxoAZpWR2HFbtyENOl9CrV7XFCBs-MY,635 +babel/locale-data/en_VG.dat,sha256=sqiVUOEtDV2pAc5DzGl76qV1mL5K-UWhMRzG-Ff6r30,616 +babel/locale-data/en_VI.dat,sha256=AGffygfBAafQMxKAAoxt6kwmbES4R_ybbXRV2tHpy04,653 +babel/locale-data/en_VU.dat,sha256=ji2cRB8B-c8uJR7L56HzbkiTR8eJZziVTMcEzuiK2g0,636 +babel/locale-data/en_WS.dat,sha256=gowKsveED3UIWyLgnSWxyE6qq87lH2dMgOVLDGCEcBk,656 +babel/locale-data/en_ZA.dat,sha256=uoSLsVWiMu6x-DYwISGhEwveg0kVsDwgnIhq2Jn0hXo,3411 +babel/locale-data/en_ZM.dat,sha256=8y814a5GtYcC5gvh3YBlcYf5Imr0yNEIV_v8jH3SXqk,885 +babel/locale-data/en_ZW.dat,sha256=GUDDEesQRkUuLyyDlnJ3MGlGzG-kDPxt8OTIqVRKDyw,3320 +babel/locale-data/eo.dat,sha256=kSIbuCrTJF0ZuNV8FzhyheJT33iKwteIHLaYgdfPO4I,101326 +babel/locale-data/eo_001.dat,sha256=3-tP9zaTDfbBBjpd2qKVIhS7WWbmtVKGGW_BmGIhrHQ,850 +babel/locale-data/es.dat,sha256=c62rMW3rHT7taFSTKPt8je50jtFex4sL8nvuVey9224,191377 +babel/locale-data/es_419.dat,sha256=s-JLmJhGKoIbnsjZm-6UXnDsAsgFhIKk9OJk7SPR_oI,27973 +babel/locale-data/es_AR.dat,sha256=rFeyqcUDLb3yYYWMuCP8R4O1n4rT1XCeayaIaoPaOPY,7087 +babel/locale-data/es_BO.dat,sha256=9zgEUshMb69M3LZS_3TOk4kGOt73fnB2UWWR7HPFAgk,1477 +babel/locale-data/es_BR.dat,sha256=oCD_7IoQ5w-C-YXpQ_wBcS0wUkcY268QDcg5y7Vo7KU,1207 +babel/locale-data/es_BZ.dat,sha256=Z3ZskA42UBcpOPDI66F6YBTvzt-g61PmxhiEpdGXpwA,1206 +babel/locale-data/es_CL.dat,sha256=tpiiCMc68Ab3YYDqi_zh0dw_IfsZmLciylujbf72XqQ,4848 +babel/locale-data/es_CO.dat,sha256=_ZNmqQnEj8EoNse4lJuK0Qrg1ZnPSgUVCPCNo7K6FE4,7436 +babel/locale-data/es_CR.dat,sha256=GknsR2O3LbFhHagGvUvgNgBVCVbXZxk-yzivTPR-Q_0,1308 +babel/locale-data/es_CU.dat,sha256=N-TqpCs34sjlEMeJgvrfOSpmnnlGxuuGtSBSaUKDbvg,656 +babel/locale-data/es_DO.dat,sha256=HMY1U9RiyiBlQKc_kUERQ-GHqWCgjaqrBY0mKUE9Gp4,2700 +babel/locale-data/es_EA.dat,sha256=V4UU6BM3pjbuEleLA_MaJtWl8IejNcHzUM660pfKa80,616 +babel/locale-data/es_EC.dat,sha256=i6oUe_5GL6b8ZnbnORW1csbq1-qgo6VN0Z6x9G2xIWk,2941 +babel/locale-data/es_ES.dat,sha256=fd1TSjdlf2lSMxwi0NhYGR2eyiABBaSJvhLlwLO46j0,653 +babel/locale-data/es_GQ.dat,sha256=a7A-hqM9wNt51_CdpjzU8aT9KEeMz165-MyhkWyqJOM,638 +babel/locale-data/es_GT.dat,sha256=zk_WqHmzYsoIAiErSYC9G62MlbgAuySSTAPZLtOX__c,4132 +babel/locale-data/es_HN.dat,sha256=71FLaDfVW5pgllY-BoQoPaDjJ4lvhs_Fi9EBmJQjZj0,3051 +babel/locale-data/es_IC.dat,sha256=hwey8h4GM8i25i-YZR_K-jvCij95wZqr0zdTIKauP58,616 +babel/locale-data/es_MX.dat,sha256=6jxhdWleuQwI_O1FZOl2I5DEwUs3oXjAhJhyBmBCMGM,25204 +babel/locale-data/es_NI.dat,sha256=cF62A52GuQD-QU9-jBmK_8AJ5pZ1W_nJ7NwraSAhMvA,1227 +babel/locale-data/es_PA.dat,sha256=fBNNQGv937Iu4OPHxSuYtPu8M-u6xhUmG0LtMbxDC0E,2775 +babel/locale-data/es_PE.dat,sha256=G6ZcerCUvlSGQmeysFNtMNJEZwPAerVqwOG2hHUpZ_U,7244 +babel/locale-data/es_PH.dat,sha256=fEMZmVPQb97bBjEtCVMLn9JUtosTePmRuQqhpfNYkZQ,1248 +babel/locale-data/es_PR.dat,sha256=wPOPeV-O9rnLzNHObqs9JEUo2ouyqmaiXqtsxHtKpWM,3307 +babel/locale-data/es_PY.dat,sha256=KXiw-rkQCdcoeTv0t0SnA8ioZrHQYfPxen8491aSq80,4442 +babel/locale-data/es_SV.dat,sha256=JM9uw_EVtB6J8bH_Nz0GL_yr6ehay4uAPm4OJIiUmfw,1260 +babel/locale-data/es_US.dat,sha256=ifZ5PL9mZhnqv3E6uRoRS2Sqlkgyz53m2RJs9wDcpMY,22533 +babel/locale-data/es_UY.dat,sha256=31KdHVEWnE6fWuHa7KOdTLcmlWXn8M5liZZMDWlaVrk,2703 +babel/locale-data/es_VE.dat,sha256=kyxdQ51DhLNf6dQauAPw4vOlpjftw9CQgc3yH_uAoKc,2509 +babel/locale-data/et.dat,sha256=eTq6nG039wKW81vnFtx-qzaWVo0wiUey2Y2ZUQLl49k,175652 +babel/locale-data/et_EE.dat,sha256=JpcyzIDpGwHlLSiInuYO3HfxkMEFsDPBeMc-B8Rj3MM,653 +babel/locale-data/eu.dat,sha256=lEQD4nzng6_Fmu8zQtkrvoFWV5AvUaCpOaAlv7vknt4,178023 +babel/locale-data/eu_ES.dat,sha256=ABS9lrwjRkWenVB4fNrwKpr0mOuMF_eOlOrSrobcDl4,653 +babel/locale-data/ewo.dat,sha256=tHyC9Uj5Gkz_nGEiTCFjXn3veej5z1GUhr-j-Dl2vgE,16881 +babel/locale-data/ewo_CM.dat,sha256=likxlZJqzYOVxcG4oSUQLTiH5DjGjdAFsJPIjnnj2Iw,636 +babel/locale-data/fa.dat,sha256=ci_F95dki3wTXhl10yqZP7IomDg1FHIOArxqTEMUEyk,192269 +babel/locale-data/fa_AF.dat,sha256=3lXNqz0r7fjXSYzqZBKGD5gtS0cPs-lx1ExY_s1ucfk,8639 +babel/locale-data/fa_IR.dat,sha256=nPuexwzRHDb9TFMjILK_V43sVIzbcn_fgp0msY3eFQ8,678 +babel/locale-data/ff.dat,sha256=gT8MoHYFJcGDoVjBCYPzsrkMTKylOjXkE64Sc0L-rvM,15911 +babel/locale-data/ff_Adlm.dat,sha256=GbwYyVBbA8SAJP-1BYMW4b3HXivgi8HvRDzIaKuJtvg,326252 +babel/locale-data/ff_Adlm_BF.dat,sha256=eYQLbHvxtEqcJCMbXSkqN2dRSuYHVc6Tg9kOUKayJQs,637 +babel/locale-data/ff_Adlm_CM.dat,sha256=b3VqkcWqXc7s1hCxN60dd-yBRXkcXfMMQ0uAJKmSsg8,656 +babel/locale-data/ff_Adlm_GH.dat,sha256=r1CRe9Z8kZ3B3PDxmZQbvgpsEcY3OjJek9Z_S8FBb5Y,1236 +babel/locale-data/ff_Adlm_GM.dat,sha256=ZM17aBc8xznEjoP4f7HRCHaKn-DIxCSDGkL6GvDYvDw,1232 +babel/locale-data/ff_Adlm_GN.dat,sha256=HWkLsTj8Sd0fWYuYysHhShl08hg0iAvSXhPjy-rUjB0,616 +babel/locale-data/ff_Adlm_GW.dat,sha256=vIUl_C4PZSWmypeEmnsAUbGN2dSquM3nEOdBs22_qNE,637 +babel/locale-data/ff_Adlm_LR.dat,sha256=277rY6zAWlxetFe0D0JpVEY9MNcQiJU9kt1jRoiXE7g,1232 +babel/locale-data/ff_Adlm_MR.dat,sha256=qz618w1XY_zwZ27nB6LBgbzsjsh4UinfnuOVx2bqLgk,1233 +babel/locale-data/ff_Adlm_NE.dat,sha256=waY1EIQKuOkVzrtJ-t-5VY7cK2uKL0SIBO0PQbx4x9Y,637 +babel/locale-data/ff_Adlm_NG.dat,sha256=9UdHELj5G90YopBKktxDyWVQyLxl5f7P3ToBElwlvCo,658 +babel/locale-data/ff_Adlm_SL.dat,sha256=7jLKofwQBRggP61keprrbfFGXvrcJPIOvwEVmUNjcTI,1233 +babel/locale-data/ff_Adlm_SN.dat,sha256=nGazz6Tun3D-XsO6SljxQeeBuzvAb1sBQw21IbkDQKo,637 +babel/locale-data/ff_Latn.dat,sha256=APyNmlFYRwCQOlboY58qsqiF57rrI3F4fHu-uQTdt4g,866 +babel/locale-data/ff_Latn_BF.dat,sha256=Nym5keR3dDPMJBFr7ZyW2cuuY-qTMkdHzXnIHSmfD4Y,616 +babel/locale-data/ff_Latn_CM.dat,sha256=rWaVAF6D5gv_hDhNmSGbcWGpPV7X4gp53FBMXyMjT-8,635 +babel/locale-data/ff_Latn_GH.dat,sha256=-kMvFNIQyikTD2dFakV81ghSR2H584AigLgtmrzlhN8,1231 +babel/locale-data/ff_Latn_GM.dat,sha256=1Air8sk0wb0McqLZXDWIYQeiKgPusBaVjr4zk54IaoU,1227 +babel/locale-data/ff_Latn_GN.dat,sha256=7R-6dXYMBdveo2-9Vc7CjIA2H6y_hGJVbfVGIaJUgbI,636 +babel/locale-data/ff_Latn_GW.dat,sha256=2kbqhzcDeSPQxefHxynmrv2tmFVnWdRNjO7I3AZ6lxk,616 +babel/locale-data/ff_Latn_LR.dat,sha256=MQxcDQnMZC5hN52abzqCjVIL9Z5mA9wOyTqGPP4Oj0g,1227 +babel/locale-data/ff_Latn_MR.dat,sha256=CDUb5owq0Ntsd2lIZJejvYxOjl-NOUPP-v3tgVj1_qU,1228 +babel/locale-data/ff_Latn_NE.dat,sha256=R8C0fI8ql5XpnT29CrwBG8rgdHB1AASDBSdvqIqIQ1o,616 +babel/locale-data/ff_Latn_NG.dat,sha256=00rtiYbJD_ci8-0M4ye95rzGpbyX8qQ-dmFFIajAtRQ,637 +babel/locale-data/ff_Latn_SL.dat,sha256=UaWOeokdbSoxG0JUyyGWkDyz70sXy-cZbc3lXZxmQK0,1228 +babel/locale-data/ff_Latn_SN.dat,sha256=mPVac9iCnwNay8c7Ko3uUvg48lfyfEZrvhpyEFFGmFk,616 +babel/locale-data/fi.dat,sha256=Vd-z6RH112cP0k5AGOlAoYU1U0f5mpQQkiHypB844Jw,204853 +babel/locale-data/fi_FI.dat,sha256=cpbJU4KKG8OWbmLnc9TKTdzmpoi2AJh_Pu1cHhDwVSM,653 +babel/locale-data/fil.dat,sha256=g0INTCXppDgjTGgBHy9hqQ8Mcw7G9K3XLTLyVshWuuA,144724 +babel/locale-data/fil_PH.dat,sha256=JjoZt3zNxy1X6RnHoxUKOMzT-xMh3tGZBRq8cvrBwqA,636 +babel/locale-data/fo.dat,sha256=4P7HQxbB7uhYUyAt1rFID1mHXUe9zlQBvWCt8LDRhNM,148411 +babel/locale-data/fo_DK.dat,sha256=HUuarLNFSsV_gN2fg-OqrPr-6Ry6KFgitCp173jxAOM,674 +babel/locale-data/fo_FO.dat,sha256=SLeNQ33QTZ92kXCzQcFfo-OgwZVFPJKIeRivuvz0zo8,653 +babel/locale-data/fr.dat,sha256=HvPCR3ft-_VxUpJsVp1wKU8aiJ7eCKZEz9XZVlM5jek,213357 +babel/locale-data/fr_BE.dat,sha256=ODfkJC5kWWEEkjr1UV8AaLtKKc4sPCUxlIGkEDkTr6s,1085 +babel/locale-data/fr_BF.dat,sha256=7piWVeMMrZ-YbO9MNqJAu7ZeIdueVnhg07eeMwJHy94,616 +babel/locale-data/fr_BI.dat,sha256=R4kaU1OtMcNIcibkw8OFGP6FaYMxt2uKMRyidkEqbiA,637 +babel/locale-data/fr_BJ.dat,sha256=sel3fdwegY5W4NRg32t4MuXoqpGqTAGO_ffCF2NPggE,616 +babel/locale-data/fr_BL.dat,sha256=n-MRZjeHNkRMwCtGgPZq52Lj0ijntBsxfskC324i2k4,616 +babel/locale-data/fr_CA.dat,sha256=Nu60QdYyJaiQnz1EbBLSWIO86Rf57q3FWoU7ywZMKQ4,72420 +babel/locale-data/fr_CD.dat,sha256=HWaY-wrLseHcf0fGlRkAWt118iQjRZaTGlqs7G23ZrU,1138 +babel/locale-data/fr_CF.dat,sha256=wxzk0Nf6_pKO2VDivQprUY9WvaxRSkrfQBoUE1UVrAA,616 +babel/locale-data/fr_CG.dat,sha256=zP3HTP-nqZjG-OD_DLrDEPPbB3t9g4T2kHVzI-HxhaU,616 +babel/locale-data/fr_CH.dat,sha256=DiO5jczUyCwTWpWSoxVHE6kLMXQPru-XotJQLDInjKw,2876 +babel/locale-data/fr_CI.dat,sha256=TrHL8hQRuJI3Auxe3aTHYa_rljyUvT_s0q-jdSJflJM,616 +babel/locale-data/fr_CM.dat,sha256=31oDAmeRb3F8a4fvVn1QJyIbrla04w8kfC4oQO0vwkk,1970 +babel/locale-data/fr_DJ.dat,sha256=Jqu2FOJQD41B6fLSBEc_Wf-dIQ-B6MWlT0m1buQnmxo,1248 +babel/locale-data/fr_DZ.dat,sha256=uOe9lbjO51q30G0UMs5nKrMLta5CtLW2Fg4dvsfRX3M,1290 +babel/locale-data/fr_FR.dat,sha256=bwtjtv0NG-lxlXO7fJd6r--l38WKaQmqlmn7PpXJdJs,653 +babel/locale-data/fr_GA.dat,sha256=rMwS-msXjMO5T5k1BP2B2S9B_dYvDHlP8HMS-Lq8XU0,616 +babel/locale-data/fr_GF.dat,sha256=x97-H7H0h4_MUTpjMdrkYB790vBvPKKxmnUTu-FOwLg,719 +babel/locale-data/fr_GN.dat,sha256=6KVlBWrF76ZhgyYg47F1TcR8_fbE5Ii2pJzPhmbr3Pk,636 +babel/locale-data/fr_GP.dat,sha256=W99xHjqPQj_NJ1X34ismj8A4xeZcHu_vyqH8tIwXgeI,653 +babel/locale-data/fr_GQ.dat,sha256=Ogf5NI4zMvbVROeIb8F6h1NxJNKOqY4fKU6G2SPJXDk,616 +babel/locale-data/fr_HT.dat,sha256=S2Vvt2b9Wu4dY5Tsd9xxFAx_RLcWXCGf_XeSSQFYbKQ,1836 +babel/locale-data/fr_KM.dat,sha256=ChLpzCyulxYg8Qmm5HK8YkK203GRSUEBNPSl8war7lA,636 +babel/locale-data/fr_LU.dat,sha256=0sYBhn8-9lvfOrU-ZEIbnO5v03qQOQq4wXCKp70b2sw,729 +babel/locale-data/fr_MA.dat,sha256=Um7dK7MvHkPB_h_r0pk47rMFCng1LWFgIekS7cDX9ZE,1098 +babel/locale-data/fr_MC.dat,sha256=IfTCZ89rqHzUxdKyIz86Zs_VUH2zc5JAvntdhyKsNR0,653 +babel/locale-data/fr_MF.dat,sha256=xU0grNJKWgpfArwpLt7vuOpSTSkyPx3o6gFkmZLCxd4,616 +babel/locale-data/fr_MG.dat,sha256=A9zYBbE6gIWuGDPxv92ReIcR49gtIEYaEkCj9ZONHHI,636 +babel/locale-data/fr_ML.dat,sha256=-PoWsHBtnaZisIOqZyaEFmwooRXb2R2xEMe6DYad_sk,1153 +babel/locale-data/fr_MQ.dat,sha256=45qWAev4p2ZaFVky1Qlrw5q907PDfbRJq9q2UACahm8,653 +babel/locale-data/fr_MR.dat,sha256=mVSOABz3cRa1-HEeUKW13sLw1ZR6PmB9h_DPfZsK4Uc,1228 +babel/locale-data/fr_MU.dat,sha256=c4ERuS1GI8h7waxcW6BRtloW7BThLlgqdYTf31Ioh_U,636 +babel/locale-data/fr_NC.dat,sha256=Su2DaGZPnLCaONmgosMYke9kH6dP8xJ8wJF6P5xrJq4,616 +babel/locale-data/fr_NE.dat,sha256=D6ghYGdDXlhaVQ5gAr8wWHxEHYR7mBs0QaiNL5nfxWM,616 +babel/locale-data/fr_PF.dat,sha256=oPN2EBo1hpLjlpDYPB51qEAx_4HNMWG5-Bh9JN6Kal0,616 +babel/locale-data/fr_PM.dat,sha256=gEP6Ay7r-p7uIjOGXP8eCa62yujlTBntENdeNd9KKes,616 +babel/locale-data/fr_RE.dat,sha256=pxNaLi-BUbe3E-Xf5ZXPoeut9NdT0D9OGEgmoSWZEr0,1055 +babel/locale-data/fr_RW.dat,sha256=gP8mpM7gx_X_9NKDm8B9PuTg83jwT5Nn3NOKPpVhi3w,636 +babel/locale-data/fr_SC.dat,sha256=qvz0N08LKCsPCPDZK1NmAhhzmaszBQ8mSlDbAsEwAoI,636 +babel/locale-data/fr_SN.dat,sha256=n6ijBmMl-ztuCLSePI-Q3yIyhzejn7KvRw5ZqjDoXsA,1018 +babel/locale-data/fr_SY.dat,sha256=tpDovgZ3aK7OumRwbntHTknV-tRL_HjcPK1aQINC-x8,1290 +babel/locale-data/fr_TD.dat,sha256=1H47veWKRj_iV9CxHMz7x7tfrL8IYdQaRWhTV455efw,1208 +babel/locale-data/fr_TG.dat,sha256=r_E5Vnj0Kb5CG16ApHFls4UreDeO7-o3uzpQdJjpbRM,616 +babel/locale-data/fr_TN.dat,sha256=hcGRJ2UjuFEFJqb1BDglGsXY8jCEIdM8--OrU3OqRhc,1228 +babel/locale-data/fr_VU.dat,sha256=FYYsiaoM8QZeClMnQzLiLZNXW9hULz-rCgRTYWvrIvs,1228 +babel/locale-data/fr_WF.dat,sha256=jn6VFRw_qpJFxpO-I6C9nhz_wRZ8cV2b2XRBgSzi710,616 +babel/locale-data/fr_YT.dat,sha256=x0WpQCLoqrLY3_nCF7JlcuekkoUtcpE4llC8kRmeRmY,616 +babel/locale-data/frr.dat,sha256=Itmd93DOwAMGcTkWE83BWDS1oXEVckX0EE6mSiOKyoA,102994 +babel/locale-data/frr_DE.dat,sha256=2ohnodzECbJBUTEta36Ts4amZKl2_LDhuD0-Hu5JBo0,654 +babel/locale-data/fur.dat,sha256=pvg46WO2uwbg27leWyWYXkoqYFiaE1u9Txsd_iZJeC8,32441 +babel/locale-data/fur_IT.dat,sha256=PpDH1opUDjndOtvpPadiOtITGyGxKZY0ziEhb-4HKbE,654 +babel/locale-data/fy.dat,sha256=zJce96OaCoG-oKfhQFisyjgVEK3pNVtF9RT-f9h7y1g,108200 +babel/locale-data/fy_NL.dat,sha256=I_M2hFVCAaVAL90yVMloW2eYmzO5C3NZztfEDAes3_I,653 +babel/locale-data/ga.dat,sha256=rx5MLgtNUisB9V7gu0e2dNyCXF2H9KhLVULuYvLNUh0,261523 +babel/locale-data/ga_GB.dat,sha256=NKphVJhmH8egga1dgfO-hSRVFhbxms2JvGMU3ybgj_Q,653 +babel/locale-data/ga_IE.dat,sha256=P948T1Tl4B7kL1T-uxii27q4IWO3yQQ5emTLv7iHvW8,653 +babel/locale-data/gaa.dat,sha256=W9tUKmjglnSUxVlpiGA0clKmagjmzgF1Fwj_UPmg8Ik,33806 +babel/locale-data/gaa_GH.dat,sha256=FF7vo24vzA2ru3t8py246uanyOqyoSkpD65F7KFTXlA,617 +babel/locale-data/gd.dat,sha256=AtSvR97H05LvFNLHpLsb7tOgKYaOjYvWFOLaDDH6xMk,280763 +babel/locale-data/gd_GB.dat,sha256=weaRul21diYZoqLk7SXacBy8GD1bH30cQO6AHejilPM,653 +babel/locale-data/gez.dat,sha256=6gIHA3BFc3-A6-dOq0pc6bqP2ej6kmG9Xvwa0EEhoMo,12743 +babel/locale-data/gez_ER.dat,sha256=MKpSW7Upkpe42Q2mbJStbjtnrW6Kyo8Tg0zXAxnHWqM,638 +babel/locale-data/gez_ET.dat,sha256=93QTSMihpcfFyRhttFplgAcsAhzrPjlbrz5HI4feRSc,636 +babel/locale-data/gl.dat,sha256=N2zvS1gg8f3l6SOLqIIiKCBsbxAcArFclISemEyGx7Y,153642 +babel/locale-data/gl_ES.dat,sha256=us8xWRQnNTO_KC42-FOU2cnLCNl96591qnxZ4exq5Fc,653 +babel/locale-data/gn.dat,sha256=2CbBgzd3kHlm8uIpKVV3yurmEXB_yNUfM2NKTFV95dU,2477 +babel/locale-data/gn_PY.dat,sha256=tArgCo85T_BUNmPc4sXVRz8X5wcHJmBS6XP0r8dPgA0,635 +babel/locale-data/gsw.dat,sha256=TGHS38pKauNWMdFFsCyCJ_9p17aLt14jMA56gxTDBM4,95265 +babel/locale-data/gsw_CH.dat,sha256=fQlRCd1MJ_gI6rcnkrU-lUE7ZM4syRLh-suMRT0DCOk,654 +babel/locale-data/gsw_FR.dat,sha256=vHNQ1q9Zyf6xLfsJpmqpaX1iHerMRt1dsJSPQdQDoTE,654 +babel/locale-data/gsw_LI.dat,sha256=dsg7TA7MECMrbTXC7i7zXIjrEoqrF2jZ_ckGiTMholo,654 +babel/locale-data/gu.dat,sha256=5A_T6q__RzuWdGg9Sfte2wOqRw_Adz-LJMJJzFJ87cg,208242 +babel/locale-data/gu_IN.dat,sha256=shXUMyk4ogB8uJPazALoReGyLqRXSYOeVBVMn313_Y4,658 +babel/locale-data/guz.dat,sha256=491I3x6EkMG0roSEKI3sHxtaweXWNXhXyk4vsG-LzDg,15427 +babel/locale-data/guz_KE.dat,sha256=Uz_x0_YFKJHDkSd46SvuIfO7W0c-uqYnasmmLNib3WU,636 +babel/locale-data/gv.dat,sha256=Ye3v_q7SrnCeiOVw1r7NRcTtUZ3RwOr1ZKs6skxYTos,3960 +babel/locale-data/gv_IM.dat,sha256=bXOsSoV303kSUECHz2xd5HhASRI-XCjqjxnxpk0Bt10,634 +babel/locale-data/ha.dat,sha256=fVfNqCuIXaCTV5E9XNMNVROCwgalJ8f4MKfEvKun3Ao,150526 +babel/locale-data/ha_Arab.dat,sha256=AUoCyyrZDTrf5SEZ2SRizimC11NMSCyonIkMRer1PxY,2197 +babel/locale-data/ha_Arab_NG.dat,sha256=oYJYqTkaDX2p4IJ9j3sEJT49dcVn_f9JyjO0rUJTmAc,616 +babel/locale-data/ha_Arab_SD.dat,sha256=7DD6pzCmpEdjwbTMAbAb_vJey6Qh0tIeSs8LjEpJxBo,678 +babel/locale-data/ha_GH.dat,sha256=Cwfuh0nEMJu4LGZmdOXJHp_5fZRdpTIODJ_Tpd83uPM,1231 +babel/locale-data/ha_NE.dat,sha256=glKzRtSaovyoiyBc-vjFbJ0wNuxfga49w_zeK3ANtvU,616 +babel/locale-data/ha_NG.dat,sha256=oYJYqTkaDX2p4IJ9j3sEJT49dcVn_f9JyjO0rUJTmAc,616 +babel/locale-data/haw.dat,sha256=aNXNxqponTYIxzV21MX0jIzG73qbhbNO6px2WGael6o,10375 +babel/locale-data/haw_US.dat,sha256=W2qJBTNdETpoN-Ue0UYbqyblj-gIbQNOUMr01RwJ2fw,654 +babel/locale-data/he.dat,sha256=o6AACO6FcY_o6bcUuLkcWgbWIT77K8N8BTBDLY0hk7A,217062 +babel/locale-data/he_IL.dat,sha256=KpQ8sod_QOZ94Tr0tfhlATzVVsrqXck6eq7txoQgCbc,678 +babel/locale-data/hi.dat,sha256=TZ6kgfAZH0cuoX3Y7FpTEFJUEmlrSGccY9nWPDk27C8,221948 +babel/locale-data/hi_IN.dat,sha256=UMkxSHgYSdX-hPgYa7rOOzft7xtJwcM0m5nTphUe9xM,658 +babel/locale-data/hi_Latn.dat,sha256=Dn0MJdILlsVYU9R5BJFSPGGr1a35jF4k9ebJMKLX69U,30945 +babel/locale-data/hi_Latn_IN.dat,sha256=UMkxSHgYSdX-hPgYa7rOOzft7xtJwcM0m5nTphUe9xM,658 +babel/locale-data/hnj.dat,sha256=ryXdpBMEUzFyZ_u_YiqAK689QOsEuUO_Auyuhty_4jY,2117 +babel/locale-data/hnj_Hmnp.dat,sha256=6GekZXtZVXtGcI_zk6NqXq3WxMnOx83IpRlJOyUws6s,746 +babel/locale-data/hnj_Hmnp_US.dat,sha256=OydEowg2bNVks5nd5ij7ZJqqQT0ztYv4HL1ZZ4gNajw,654 +babel/locale-data/hr.dat,sha256=kdzKpWmyLAIFw7D5DPKG7aHUQjYlM5fctcBLRoHeujg,207636 +babel/locale-data/hr_BA.dat,sha256=E_S8FRdn0yszTWFg9RlqTV5zEwCc7-qtod45Ud8LHyI,1188 +babel/locale-data/hr_HR.dat,sha256=PqaNYKNdTFgfgmeiINdYp2d3q8qpfkCUs1y4ZHmsphA,635 +babel/locale-data/hsb.dat,sha256=w5bDS336hxU-RChl4QgaayXgMgN_VBUh5GJXznPI_Bs,206887 +babel/locale-data/hsb_DE.dat,sha256=_ELT9KBswSqeyiwbcsP9nFY793FE8PQzEu_2w78Amro,654 +babel/locale-data/hu.dat,sha256=dmS-7GInw1eGbifjgMyXc1m1nBg6dK6IOJIDoLb_Epo,147032 +babel/locale-data/hu_HU.dat,sha256=r3RZskHfUdk1Ba226wqBHAoRMXorlh-IkWLlnY_mBTs,653 +babel/locale-data/hy.dat,sha256=R6n2G8QdkkxlAxBgpTK7l-cPhwHg03vIG1pwmWVrCV4,202316 +babel/locale-data/hy_AM.dat,sha256=r1Bb6WWX2QxVlLP5vRVgox0HgoDOosA-h3oYKLeZMIY,635 +babel/locale-data/ia.dat,sha256=Wa4yDTDv9TA2Imo69v_49GqgV8LvQP1iuWGq6LFAMOk,133538 +babel/locale-data/ia_001.dat,sha256=fzAkHPu7PNj2TOl-kQcJLIdlEK6z-KfVFUVcy-YF5Zg,941 +babel/locale-data/id.dat,sha256=CfCCzK3VV98obeH7x1Ueax-xYczfCwextwJ7aetTQ5A,126977 +babel/locale-data/id_ID.dat,sha256=GvwO35RP_Y8vswE60ItYMQa7XUOBXGLk8tvL8kSJkY4,635 +babel/locale-data/ie.dat,sha256=9nzS8srJ9Mze83D7nKqXSlGvjOtu7utOyAJCLaffEm4,77957 +babel/locale-data/ie_EE.dat,sha256=93dmapYNE5HEAfIRwL_HjNV4fpCJhy1CseggbriCXL0,653 +babel/locale-data/ig.dat,sha256=ts_BawFD_ves9B7kX6GAIxwBY8FP4jNmkZidIdAVaDA,78550 +babel/locale-data/ig_NG.dat,sha256=2MR603ed1L8Y_aZhXPk7K7J8bgQPE4xvvpn8R9Jodiw,616 +babel/locale-data/ii.dat,sha256=WBkpukOuFcoryzq0QnHhdafGfad_u9vdJy0JO9vo-iI,7477 +babel/locale-data/ii_CN.dat,sha256=Vr_Oe23kFnz-EDfl7XeDv6GeTY0LSK-VV6UJpzU8yJE,635 +babel/locale-data/io.dat,sha256=laqBHV7QB933VjLibEOmRezXV9StwhKVzKvWp8e62YA,932 +babel/locale-data/io_001.dat,sha256=T6zZvvSVOcdpy3fhXbdPAWmHCvDTluick2U8_hUDu2E,912 +babel/locale-data/is.dat,sha256=r9gnQpf81NqPVskVKUiSGWa6VU3_NXzJcna1Gho28d8,163097 +babel/locale-data/is_IS.dat,sha256=f3tUj_oYhQ6_tGnn8yda4PZfqChiHmcZXo_nMDCtzSQ,653 +babel/locale-data/it.dat,sha256=n8pxKaMuS2ekD7EOSV-3PmVfdzm9NqxF5zA073x5itU,173579 +babel/locale-data/it_CH.dat,sha256=Tvtge1s4BcbCeKJmPBiSbtbf_qv8wrw3Eb4Dc1sF3dE,2942 +babel/locale-data/it_IT.dat,sha256=-e6EK0A6gEd_G4_BHlmd8_caQ-k_JrEemNgdA0czhsQ,653 +babel/locale-data/it_SM.dat,sha256=xjE63TohE8bjkNiOh-BhOkuB9YWpQ0n2rrBMJoAql5E,653 +babel/locale-data/it_VA.dat,sha256=wwsl_eAmNu9m2JPZnpBwFVh4gsk5AM9lSx8UAXhrMlE,653 +babel/locale-data/iu.dat,sha256=vZIG1AsqwcrTA6zfwBeQydVAn5qDQXT8yTK8wvbaVAA,3209 +babel/locale-data/iu_CA.dat,sha256=C449KxuskTW8w7z56Vv602QRYsQ90O0WbazR7VqlDa8,635 +babel/locale-data/iu_Latn.dat,sha256=oYsdWLomYvVJg6kIQIKYOdSGWyrEHftc2U9bZ0sHFD8,904 +babel/locale-data/iu_Latn_CA.dat,sha256=C449KxuskTW8w7z56Vv602QRYsQ90O0WbazR7VqlDa8,635 +babel/locale-data/ja.dat,sha256=86xPBOY1H1e0fLC6g_61R_ipS4QGbB9sXst_JwwdAWo,179695 +babel/locale-data/ja_JP.dat,sha256=o2pbFmlkEZjV7CHE9cYENYVv2kQA8texhNDhEecgQ9w,635 +babel/locale-data/jbo.dat,sha256=2eB9ymOoFb8abB-v7ISAGcAg4HEEMmpuseHIXFgtqkU,1008 +babel/locale-data/jbo_001.dat,sha256=wwLKEXJOwULYD-q92kQdvpiwW2fgis5YcsP2mT4tnT0,746 +babel/locale-data/jgo.dat,sha256=9X63SqWqQ7TK7Z2RKaQwFBP9sMog_4cQN5Sr_KBlyL0,9031 +babel/locale-data/jgo_CM.dat,sha256=1lDyjZLpK6fS6oSpfnVLdYd2IUgGlUQfF53BDdK7TLY,636 +babel/locale-data/jmc.dat,sha256=lRstulkfCj6kDI8VLH_3GS4awAqVsylMU1hvvWH7kJg,15374 +babel/locale-data/jmc_TZ.dat,sha256=fODp_OsVygbH0UoRsx4HHcXrde02bRKc-hy80sDkcK4,617 +babel/locale-data/jv.dat,sha256=SucM3ZDoHXO7PsbOYNLdx0UmQqWUll2i1hDqn-zJY9U,103138 +babel/locale-data/jv_ID.dat,sha256=G3zTSeqWMngemp5lAcghudTcv-i8egZS12P0-T1DNXw,635 +babel/locale-data/ka.dat,sha256=dIskDjc7ZdRZ1rGJIHxOP_47WO_cdjLDx4eltYETbUE,230234 +babel/locale-data/ka_GE.dat,sha256=zzw4NZBk8dsHIBeEzB3Vtf2_1nC4DhqblLoBUXfNHN8,635 +babel/locale-data/kaa.dat,sha256=KjLQ7_UT8pqVmgqy5BXJHe9hbPceKi6AIyefM4_0pYU,17585 +babel/locale-data/kaa_Cyrl.dat,sha256=zW7tFPX2CPo0NwR2o1DCKu8Tsvm-Aef9RAuy6AcaJXs,693 +babel/locale-data/kaa_Cyrl_UZ.dat,sha256=DugGAZvImbUSHvvGmGw0ekXXBj34vEjSkHhSxVXbc-g,636 +babel/locale-data/kaa_Latn.dat,sha256=zW7tFPX2CPo0NwR2o1DCKu8Tsvm-Aef9RAuy6AcaJXs,693 +babel/locale-data/kaa_Latn_UZ.dat,sha256=DugGAZvImbUSHvvGmGw0ekXXBj34vEjSkHhSxVXbc-g,636 +babel/locale-data/kab.dat,sha256=ykLRvdRtHpY8y7JDHOgC03KxAyvAfxzYQUl44CKKr6s,121528 +babel/locale-data/kab_DZ.dat,sha256=v6fd0kyletm1kfN2ibSUh6oddckpLHjPd_qrq6FQpd8,679 +babel/locale-data/kaj.dat,sha256=tKKi4eYHT0zEJLSHi2DefEGyTTu4RCKpnANu7NQhAPg,2696 +babel/locale-data/kaj_NG.dat,sha256=1RODrBAPxCrrlTgfhpdz-VxQkTdjJEJYHaJEsOgQsNk,617 +babel/locale-data/kam.dat,sha256=br3FOMP0rS2tAq1tSXwYq197v2XXHqWtX_TM-39QP1k,15506 +babel/locale-data/kam_KE.dat,sha256=8AQQhHQdaI4rBKXfTivg884XIDfZlbDEQM28KwvB2Og,636 +babel/locale-data/kcg.dat,sha256=dOZR1bP8sPbhD2Mk496Twz8MOivgGQoliuDMAWSO9fY,2549 +babel/locale-data/kcg_NG.dat,sha256=jCObRZa-t9Khgx_M8ps0PLbeit7rPCFtHeCOhtfCnOY,617 +babel/locale-data/kde.dat,sha256=cFVUYeayn6-kMX6PwQQ3J8rpHMql07_V7lSluInC2x8,15810 +babel/locale-data/kde_TZ.dat,sha256=2sfb3gejYM15YtZeRrc0AJgdaJhqgbVJ9c9yF9SlJmg,617 +babel/locale-data/kea.dat,sha256=o2awnBismFEWQemtUPASmZMVLlOkJNDtuD6egmvMatw,76097 +babel/locale-data/kea_CV.dat,sha256=LyzxPE6yP0f9hdD4uvu8CvEp8nmcmRDVuRI8YxBQ5wM,617 +babel/locale-data/ken.dat,sha256=PMrEuqYRQIhbpP2dPowHOL_NKKFpEBd8qNUV_NpZnDw,719 +babel/locale-data/ken_CM.dat,sha256=jNEojAAtQqJMkEq84kxHU5hGOVQCOW_hnGQScxycYMo,636 +babel/locale-data/kgp.dat,sha256=OKx1ZbBdHw75AdzrT-petm32Dj8wJdtUQH_oai7v0qI,184092 +babel/locale-data/kgp_BR.dat,sha256=FNSYAqPhkzoZzholxTydgasgkuw7jgxCfyKDbeRiLt4,636 +babel/locale-data/khq.dat,sha256=OcW4Zprt-WlTePo7DzquB-yS8vwfPqjaijpkrX5l75w,15753 +babel/locale-data/khq_ML.dat,sha256=ViqThOcZTHZBtebV8LJY-DXdWVX5jAR5pdlBrTKws2Q,617 +babel/locale-data/ki.dat,sha256=vffpb6Wy7TN__hZQH1SJYi81AxamhBnEK_XBCiF3OFc,15451 +babel/locale-data/ki_KE.dat,sha256=PNoxhP-WLxW2vH3fS8N9Nuy0tVimMP-ZAaCfLfnfDN0,635 +babel/locale-data/kk.dat,sha256=93jtpKcfiQBRbyeOyJgqETZXGOntosB2nGedgHShM1w,200871 +babel/locale-data/kk_Arab.dat,sha256=TBsZm6iLJ_dw2mNxDdQk95jELZ87mArNl12iktCwKwo,222820 +babel/locale-data/kk_Arab_CN.dat,sha256=HCoaBi32ENdpJx5usXdBR9aR__MHFeKV-wTo_53tQNk,635 +babel/locale-data/kk_Cyrl.dat,sha256=z3n_4vbUD_OcZhi9Tm8vkSu9h4RUgavZ_CFrujFbuWE,1584 +babel/locale-data/kk_Cyrl_KZ.dat,sha256=eaQW7Va1z2L34hs-D9QgrmyvOo4Lpy2uxxvxms6kL4s,635 +babel/locale-data/kk_KZ.dat,sha256=eaQW7Va1z2L34hs-D9QgrmyvOo4Lpy2uxxvxms6kL4s,635 +babel/locale-data/kkj.dat,sha256=IYeF4Uoeb0kfxooFLFcqmdIwGWbWh0MUfBpztTxWFEw,3373 +babel/locale-data/kkj_CM.dat,sha256=gqHwzVPG_Lo3svaIVe0Kfhj4DaKtstOxJJhUGvvpSbI,636 +babel/locale-data/kl.dat,sha256=D8SSjxLZliXOxyTxpC1x7jYbFQn4WbSV3lfotFFt3Jw,47570 +babel/locale-data/kl_GL.dat,sha256=c49k11GtBG8-vExsQNo_d1pFaQm0LYmXl7QcMSTEApA,616 +babel/locale-data/kln.dat,sha256=pldtRERWUhEOA85_zeFJfshpMl6gtgdELjGyNGtJzOo,17427 +babel/locale-data/kln_KE.dat,sha256=ZhkKgZ2lTdWAvWVOJEFlp4ASvjk2hcyTc2_P9s3mykQ,636 +babel/locale-data/km.dat,sha256=TueDML7PuHjlME0Bew9xgkFSFIxBOsJTzpMwyjyvuXs,176653 +babel/locale-data/km_KH.dat,sha256=bnykdq-RYJ0ZwajV5dFBVtM4anVkLjXutFdUxKByQ2o,635 +babel/locale-data/kn.dat,sha256=n0AHHLkpl4FIYeiSCEMDJF9mFr5nCgHcV8HSCOKz8yw,267281 +babel/locale-data/kn_IN.dat,sha256=rx1LJYeAP8xU6MIU3yhHLuije5OQWL_wzz5glztIdi0,658 +babel/locale-data/ko.dat,sha256=tYIinjTM5ZLw-AvWerUR8h4FRQ09LGfoRNyutl2iMLI,154621 +babel/locale-data/ko_CN.dat,sha256=U_q1api28ucaC-ABOBb822CgSq4nywhMN02uS7TCREI,1187 +babel/locale-data/ko_KP.dat,sha256=BCMOKQSzXNnjAUWfZZ8pbFoSDBed9tnnatyqIePNWio,816 +babel/locale-data/ko_KR.dat,sha256=CJqKij0oULIeCIG8McohdnhEsfJxUwmJHOpJY1bLrNY,635 +babel/locale-data/kok.dat,sha256=1pS0SXgetR-9DHtL71PAw2VIRp51a9KfxoSnw3BoxFs,179992 +babel/locale-data/kok_Deva.dat,sha256=EP15THeV74hd5G3DDXlwIHzxQvpECa67TioBGaur3l8,693 +babel/locale-data/kok_Deva_IN.dat,sha256=GiRy8Q6VCULzgIxNPFEZzABKOg13Un-DblBwXnBSeEk,659 +babel/locale-data/kok_Latn.dat,sha256=jVrQX1SeylbWO6B3ana7r9r1sRvMyOvBVwPfyXxB4uM,29867 +babel/locale-data/kok_Latn_IN.dat,sha256=GiRy8Q6VCULzgIxNPFEZzABKOg13Un-DblBwXnBSeEk,659 +babel/locale-data/kpe.dat,sha256=AHR_ZG1p518D4yulM_cs5E58iMsVXKk4Hi6k0V0IDhQ,1331 +babel/locale-data/kpe_GN.dat,sha256=7eabSFmszupn0IlzP6TRg1-7FZS9AB3JdBkDgJtIs64,1210 +babel/locale-data/kpe_LR.dat,sha256=4O2gzHn_orogC0qOiIyIox-JjyA73hEAe_fZ3Fj5tYc,617 +babel/locale-data/ks.dat,sha256=KySSApsfBfxM26TaXsK2gChd3noV1e5l41gUsaC3DA4,111691 +babel/locale-data/ks_Arab.dat,sha256=fzDL_rCiC-BqymPhF6GBX-d5uVkvZCE4e8JS2L8cjRU,850 +babel/locale-data/ks_Arab_IN.dat,sha256=-OE8PBigMDH8zQrxiaffmabm91Mi8-OVdDR2JEK8xPA,658 +babel/locale-data/ks_Deva.dat,sha256=weXN4SV8247g4MW8R7MHXM6sKk1qhf2Wmowls_zowAM,12510 +babel/locale-data/ks_Deva_IN.dat,sha256=-OE8PBigMDH8zQrxiaffmabm91Mi8-OVdDR2JEK8xPA,658 +babel/locale-data/ksb.dat,sha256=vbB4JVStmpZIu9_vi6f9-Dt40sN2wPvF9Y2JzJJ9oEY,15356 +babel/locale-data/ksb_TZ.dat,sha256=PvWBMUhpMhV27zE9BCEJW8oMqRXWK-6VwmSsXZDMTPY,617 +babel/locale-data/ksf.dat,sha256=IIgvqtnlTxMmLSk38JyZe0PA5gLEX86ZDcaVIULp7Zc,15952 +babel/locale-data/ksf_CM.dat,sha256=0OOsuUS0S_AUxUPySFQOZVbPcB3F2I4yzHZm63YypDU,636 +babel/locale-data/ksh.dat,sha256=M6W02xiGUibDW13RIB7psbwMA2wdNYQHz3Bp9XljhZ0,76781 +babel/locale-data/ksh_DE.dat,sha256=EhgYIJQaC3LGiWrcE2sdfUC8LOWJnciWORi_yoRkOvw,654 +babel/locale-data/ku.dat,sha256=beByrhXjB8iRhaNGxkQaxnUckutus3MJ61Pm-Ro8SqU,117087 +babel/locale-data/ku_TR.dat,sha256=LoNyoL-poYXgqejDzB9-zI_0TIJF3amwyzcs6UnHUII,635 +babel/locale-data/kw.dat,sha256=x5JFxfv4DvLd5Xnq5J7JlbcffhYNXwIn5-Jp3Q5QV5A,7242 +babel/locale-data/kw_GB.dat,sha256=ol7LWhJJFNoNkFEjtk4x1PpLkML9UbuVmZAuG47LokY,653 +babel/locale-data/kxv.dat,sha256=AMdZKJIlfkcpSetPdGRHX8LLN0O1s14NAtDzekSi01E,69503 +babel/locale-data/kxv_Deva.dat,sha256=MlejiNswS_LJAemj-vyEyvXc2SyminqNCWbaq3eceKk,86834 +babel/locale-data/kxv_Deva_IN.dat,sha256=Z43Yc1G4Bsmu7KukFfgXfIl1Cq2Rqa7RQQNYygE5Aak,659 +babel/locale-data/kxv_Latn.dat,sha256=XqtuF9yh0tBrDPoRpZj_1Mw-QUqt98klOlgZXh7qPus,693 +babel/locale-data/kxv_Latn_IN.dat,sha256=Z43Yc1G4Bsmu7KukFfgXfIl1Cq2Rqa7RQQNYygE5Aak,659 +babel/locale-data/kxv_Orya.dat,sha256=lsjWswZU3LWmYNE5tLn9cla-WSbdfqAoltC_OvxtR28,85964 +babel/locale-data/kxv_Orya_IN.dat,sha256=Z43Yc1G4Bsmu7KukFfgXfIl1Cq2Rqa7RQQNYygE5Aak,659 +babel/locale-data/kxv_Telu.dat,sha256=rXJrWxHMFrPeheoNt9NvKLzNoGLpNXiHpc4PsuS4tX0,88218 +babel/locale-data/kxv_Telu_IN.dat,sha256=Z43Yc1G4Bsmu7KukFfgXfIl1Cq2Rqa7RQQNYygE5Aak,659 +babel/locale-data/ky.dat,sha256=PAH_ov1xcnJAie5ltB3I_XAUJsrE_8tFwFlOjorRBts,179889 +babel/locale-data/ky_KG.dat,sha256=CdtjRR2iZgfG5pdz13MX-EHTDtN6BdLeZwGCmrZnrkU,635 +babel/locale-data/la.dat,sha256=0TAdiOxADg0C2AeIj5RRxiLgMbVO-PqRMf4GAm1M4dE,33982 +babel/locale-data/la_VA.dat,sha256=dhULocaXyJGitrljd1w688DTmezcmmY8YcoKsT30n34,653 +babel/locale-data/lag.dat,sha256=mtjtyLHLCfoUr4STtBqLXOxLnZ0igugTC9fZiyJZk8E,16281 +babel/locale-data/lag_TZ.dat,sha256=LlfEls8DDvoVp07MYnFdLBPUhkJs2kzwUDk45BZIO-g,617 +babel/locale-data/lb.dat,sha256=lUGm84DfwLgQ3vC0mL_foFf5dSZZKDTIuwCRf5BjgVs,135641 +babel/locale-data/lb_LU.dat,sha256=s0AY2-F7BqA4tMcRPjCfbzbXRbHsKnD6uIQGSkulCT8,653 +babel/locale-data/lg.dat,sha256=LnMo-qAcWHbbAuRmCTz4_nBGHgUx0aojWG7C8TFn__Q,15834 +babel/locale-data/lg_UG.dat,sha256=nyn_HKdBHGkHlbjlDX28L5Z9k59MKFqvmVL3vg222s0,639 +babel/locale-data/lij.dat,sha256=Ic982St82mPMfuQqqLswPtOy8LlglfcLYk_ftIDM65Q,129648 +babel/locale-data/lij_IT.dat,sha256=rtPerU7ltwkHHc7HErfwJfX8TDSCpTHvBzwKBdSt4x4,654 +babel/locale-data/lkt.dat,sha256=U_yXNWWJkqlepaZ9kS-FVOAHQKVmc0vhwgWgFRzXnqc,11180 +babel/locale-data/lkt_US.dat,sha256=ywWg9m80tNjumAxGNrBwYhXhc28rC4OSdyvkdXAKs5Y,654 +babel/locale-data/lld.dat,sha256=2JBQPu8T-cMVqzVOFs0Vz-0hU2RtHMyTvj0F8wPl2EU,95451 +babel/locale-data/lld_IT.dat,sha256=rHSiZg_-Ba6si2LONUCmQ841RxcX2p7V1jDSuwNNBg4,654 +babel/locale-data/lmo.dat,sha256=GYoR98Ap5hosG9bfENEA3sCkR-msF_KvxV2nLydjs9s,1659 +babel/locale-data/lmo_IT.dat,sha256=Xg4Qvye_hPNJLCTwoBuRMAvhtjQb8VlD9SaH_5hVG9s,654 +babel/locale-data/ln.dat,sha256=OHqUbo7M-600CtpOUNqq3B-Tt0_L3c2GgyR2xZhuMmE,23909 +babel/locale-data/ln_AO.dat,sha256=VNcFViSaGw3f92D60Uk7vxQ8i6Q9H1FD6paDpJh-r4M,636 +babel/locale-data/ln_CD.dat,sha256=Ao_lUWuswJgGHhVJYs2_Z8LyN4iqgYsAda8f9UarT6s,616 +babel/locale-data/ln_CF.dat,sha256=g5Jy7ovUkwcn0W5Ov5bu5SBc2iM4SWwUFwtzWzAAb34,616 +babel/locale-data/ln_CG.dat,sha256=jQld0yw63ufWbhlOt81wjf731zZluc2zWc1A65kBlAM,616 +babel/locale-data/lo.dat,sha256=hXtgT4nkqb8h669TsazfIFBsK23QNpcCZbqvb6SXWBo,189363 +babel/locale-data/lo_LA.dat,sha256=gNa7KRGYWI0h0PcHJ35bwnPskILOH-1jLjHCXP85jaU,635 +babel/locale-data/lrc.dat,sha256=GbK-bWw4TlGvGRyM4WxDujkUr7Gdz9k2A__fSelwEdk,13594 +babel/locale-data/lrc_IQ.dat,sha256=cy9DO9oAKI51NMitAb2MTbZoxtaAPzaYVGV0-xd0mGA,1255 +babel/locale-data/lrc_IR.dat,sha256=-ca4EmTX5VAi_5tDfimpevJFb-g8vAjgpe_jkDTDZWQ,679 +babel/locale-data/lt.dat,sha256=1h5TjFvNSuqrE3JntTmnLN9wW69jfkVakAIICcmoVbc,262613 +babel/locale-data/lt_LT.dat,sha256=j8dIQrFc0s77xJ4P-UGiDcaNqZW6CXOEiOh30FemNiY,653 +babel/locale-data/ltg.dat,sha256=qRaChoBWcmkL8As9MgCysSh58BP_F3US65EaA97ZgZQ,2632 +babel/locale-data/ltg_LV.dat,sha256=Gm8suGbSLaNGzzWPjuEKVdEqJH18ABue4htegryH3bw,636 +babel/locale-data/lu.dat,sha256=4TGAGkYbDHz6QAKQHHXoRkl0Wx-0LOiMkTjDgwJq0TU,15331 +babel/locale-data/lu_CD.dat,sha256=K3k0wMpIbjusgPC2W1q2sxyxynx2M78zOLjt4ikcoZA,616 +babel/locale-data/luo.dat,sha256=xJNSb25OVQogO42d-Jy2fR1FzeSfW9Swwt9Wat4EmsA,15201 +babel/locale-data/luo_KE.dat,sha256=jVUd3oO_U7IP6KmvSkVHYNmlCc8_-m2mpJMBaqArSFI,636 +babel/locale-data/luy.dat,sha256=pcTbaSctQyVgVVh4nXoXxMQGZa3Ip7hsfRN096zwEsY,15030 +babel/locale-data/luy_KE.dat,sha256=NVkQAtmDBXUws3KybnM706J3lwUc5fJo-YEjKd1zhiw,636 +babel/locale-data/lv.dat,sha256=s10obHsTYkBF99rADGvkxupv--JkRPQS2aM6sWvbM4A,201861 +babel/locale-data/lv_LV.dat,sha256=oS9W0pdiTNtMTeGOsef2K7Va2O6k-dQEtN02qCaAoFw,635 +babel/locale-data/mai.dat,sha256=Foj4spiHhuOjBZqaXQxZNCWwdCoslwB7KSX6nXQReu8,95488 +babel/locale-data/mai_IN.dat,sha256=DoclpHa5nlHcAflg286ycOjQ6trZP8uHThzcp37Y91E,659 +babel/locale-data/mas.dat,sha256=xxugbgGc3JxvM2GzV1vmKy1buszviy7NtSH-y6GdMZ4,16595 +babel/locale-data/mas_KE.dat,sha256=zH3Le4j03YNsPjt_-GL5RvPG0znnJKQ3ifbLhMZsoYQ,636 +babel/locale-data/mas_TZ.dat,sha256=VyRJlBqV_fukTqSR9Jxr-PujSA7u-MP9aXrJBVJCKiA,638 +babel/locale-data/mdf.dat,sha256=NQH9Yo77oyvxZj_93XIf0e00JDAoI2ydlCSS3klNgwk,2240 +babel/locale-data/mdf_RU.dat,sha256=sVnTxgzU2rEwl7P4d5UY6XOKv1XUCbtkEQO0cu2OlOk,654 +babel/locale-data/mer.dat,sha256=WFl2qQThWrilUbBaN4UJQb97ZrV3uOB4ZTO9kXsjtnQ,15426 +babel/locale-data/mer_KE.dat,sha256=WqDQTsO6BtbROdcj3p3omUZk8XB6HiJrIlCCj_wLPxo,636 +babel/locale-data/mfe.dat,sha256=n5lJPfd4pjC3xmVm123ivVpjg-WAkLQq9YONw1JlpTs,14835 +babel/locale-data/mfe_MU.dat,sha256=MHUOKiQPHuUbhT6hQXJEkpGDvTUyMV4btJTuiLhJQ4k,617 +babel/locale-data/mg.dat,sha256=9PbZhJ2v9RP2XSzwv7bBPTGlSNYJrUUISNkTwdF1Z7g,18776 +babel/locale-data/mg_MG.dat,sha256=uJbGeEzIn4Rh5Bh4hyV-Hs0_qez9KBeOi4hvfC3zkIw,616 +babel/locale-data/mgh.dat,sha256=ezIhzOd-qzUBRud5ZPjbtBBc_mON2HuKGu_okOahCbk,9753 +babel/locale-data/mgh_MZ.dat,sha256=aFZZ41qtARE6CeecCEx99tzW_fp-XfkAc10wpQ5YeGM,636 +babel/locale-data/mgo.dat,sha256=xyRaEmGV6Fw9nVfReQJY7KfgGwdnvQzScLiH0appsLU,3549 +babel/locale-data/mgo_CM.dat,sha256=Nsp33t4abZsKdDmIgB8tb08OsEh-7JXMY_JKCsd61zY,636 +babel/locale-data/mhn.dat,sha256=RpSxxevhvYxyX0dq1RAhIqoREyyPwKjsByXvOhQkAMc,693 +babel/locale-data/mhn_IT.dat,sha256=3UoH_l7yItN5qXKPaq6bWK_CvK_wD68zkxzLrCwxr9A,654 +babel/locale-data/mi.dat,sha256=ZlyyKSUJCtl7z94uIwEFuC6uBGWXOTM8F1TJLiJeSmw,85086 +babel/locale-data/mi_NZ.dat,sha256=RElP_P4F1HDHjSHsdt54vriFcz0TcIeT26pm-_dy-Mk,635 +babel/locale-data/mic.dat,sha256=Sp71dA3ZhTiiPTfk3b0Mm6y6CM0J-mGR0av-psrUGtI,1638 +babel/locale-data/mic_CA.dat,sha256=uZoBvRFux6M40HcGau7E6V0Pyxh45Bxi0SFngOxLWRQ,636 +babel/locale-data/mk.dat,sha256=xVJU-aubw2FQ_THzBUGd5LHR5IU1VjGkPOeRHpU-T98,216606 +babel/locale-data/mk_MK.dat,sha256=qlI1ZtLgyX2fcYS9pnTa8illdSnPmfNXORjws-H93lc,635 +babel/locale-data/ml.dat,sha256=tR0lt2uQuzmsBgXWIuRpz-AzD00S_OrvLc-QkiYAX-U,246736 +babel/locale-data/ml_IN.dat,sha256=VwCnSFs3oh7xo4-nS1HzWj_xICDzG6E-AW0Oxi6Lu08,658 +babel/locale-data/mn.dat,sha256=O3oSpnDd3Pt-dRrwdg1v245S6Iym3SBnZZ3gqOhQW5A,183647 +babel/locale-data/mn_MN.dat,sha256=qhJGIDXSglsa3BYCalsusoyiKgOj7PJ90Yi4ET2-nxc,635 +babel/locale-data/mn_Mong.dat,sha256=zqCuL28Sx1DS8LGqtC2nyTCoEpSFpF0vE3wvBxpHK1o,1726 +babel/locale-data/mn_Mong_CN.dat,sha256=WTF89EShdtBd0Vz62erFwl9KTG-GgEyzjjmKWuJCFH0,635 +babel/locale-data/mn_Mong_MN.dat,sha256=BTah7qfU9QayUb7evUKdF9c4Ptf4NqqO6SUeUql2Bak,12935 +babel/locale-data/mni.dat,sha256=aS_XIOmHRptFT_dTFuxgTvB2cEPKHJVVM_vtURTFo7A,12972 +babel/locale-data/mni_Beng.dat,sha256=7PTKZT_UE3WNiipyGHnn2SDNFNhAcqHhEJ8T0Y3O7gI,693 +babel/locale-data/mni_Beng_IN.dat,sha256=nAW_LfRtJqC6-ehdC8yyszjhibXqMAXPkuaz8POQav8,659 +babel/locale-data/mni_Mtei.dat,sha256=zdzwNyaRIil7lkjEf6ProQX0nwYlc5VF4r4gHNTUVTY,1720 +babel/locale-data/mni_Mtei_IN.dat,sha256=nAW_LfRtJqC6-ehdC8yyszjhibXqMAXPkuaz8POQav8,659 +babel/locale-data/moh.dat,sha256=FuywEiFRXQe1GUv9zPR15KOsZLr1TykmuglinLINTqE,1316 +babel/locale-data/moh_CA.dat,sha256=AXdrS_As8DrrTGs5IHmQwXoEXndFuoWoTGXPXcL4Pk4,636 +babel/locale-data/mr.dat,sha256=UG0FkufUtQ7qvzcK_WENRq6SgPy4zAcviYDQL-NrGY8,234157 +babel/locale-data/mr_IN.dat,sha256=HOu9QR1BLbciqF6BeTfWvD0L5Igwv5HrCWu9y_vNns0,658 +babel/locale-data/ms.dat,sha256=l_YQob4w4jdu0U-Z-eMYK2bPAyU0Xeekar0olT5G21s,114970 +babel/locale-data/ms_Arab.dat,sha256=9zretwvdo_9N3SIxLKsUtDVNZlmh0OhrPDxmtWqw22U,14359 +babel/locale-data/ms_Arab_BN.dat,sha256=vTEKSh0hjyiWJhf503-fwjADvTCN0AqRSzF6muL52JE,1335 +babel/locale-data/ms_Arab_MY.dat,sha256=w6ZZrz3qEmnsLsWtJA8rTohqF1cxctkzSj4WN0XHuHE,635 +babel/locale-data/ms_BN.dat,sha256=vTEKSh0hjyiWJhf503-fwjADvTCN0AqRSzF6muL52JE,1335 +babel/locale-data/ms_ID.dat,sha256=ZPgnDJgiycDHGZnN3Eg7OgZp1jIYtiZzMqm9f3LOtR8,3440 +babel/locale-data/ms_MY.dat,sha256=w6ZZrz3qEmnsLsWtJA8rTohqF1cxctkzSj4WN0XHuHE,635 +babel/locale-data/ms_SG.dat,sha256=jsfNag5WQWdejzh5hayA7_-IbY03MUVPAPDHUJyCZRg,654 +babel/locale-data/mt.dat,sha256=Xx_-as8jJEXOBAgfFX7vE6-2Qex4FWJ5rYW3DlCbu0A,49384 +babel/locale-data/mt_MT.dat,sha256=L5q5LizZQL0-FLj2U-wn2YhkpL3p7ZvjdyAvH1wwUG0,635 +babel/locale-data/mua.dat,sha256=N4zi8XyQdW_TmszNHQyn4zmia-MpCNWx_Vudoaj075Q,15861 +babel/locale-data/mua_CM.dat,sha256=Ib4K67KfndwGsoZDH0ehHA-F7V_SUj7q9VsK2juzaIQ,636 +babel/locale-data/mus.dat,sha256=zPqFwR9oCIJ0mA6evYwT75cWt_QBF-0eDZx1n6fmsDY,2722 +babel/locale-data/mus_US.dat,sha256=xcd4KKUIqBwSefSC0GGSDkvXL46eDWgWolJsGuSf654,654 +babel/locale-data/my.dat,sha256=9crk_2tyVPKBH_YWC_D0h8o1nbHH9k34e414JywM_6E,183534 +babel/locale-data/my_MM.dat,sha256=l53-D5YL-BbmLZYveFV2ISa4NcrSwTfKsRRc41Jd42U,635 +babel/locale-data/myv.dat,sha256=78-9U5eDf3V2q3Mj0MazIxh1n8sjyVjQ1O0Oyt5875M,16828 +babel/locale-data/myv_RU.dat,sha256=sncxwOpjfFkRASKHMP1ytKcjpF4w4GjU_JEVqXv84Tg,654 +babel/locale-data/mzn.dat,sha256=8Y5VGlzCxKXzBP5iFsyflLZSS1x0_3T8sqbwivfU45s,46144 +babel/locale-data/mzn_IR.dat,sha256=fjN1ek2fAzZyzFJpjX_MIc5oFOEqj7LS_SNdBOQS9ZY,679 +babel/locale-data/naq.dat,sha256=N07nsBSSEuNZ4feWvo7b4YWwbgjBwlxG9653O-OlhmI,15797 +babel/locale-data/naq_NA.dat,sha256=e5gAIOo7le-dVOAX_k2OW6gEyVfEUxcYDUJu_MBDY5M,617 +babel/locale-data/nb.dat,sha256=-bGHDSonSEdqfb2PxNC1_3jOHELm3CwV7xzEtpmWZUE,1330 +babel/locale-data/nb_NO.dat,sha256=J1UiN-AG900acMz4p0fBHtXDpS0HD6uGAi7gg9gOrsY,653 +babel/locale-data/nb_SJ.dat,sha256=XcK9_nKriUY_4k09kmgJalGTls-e5-YS-MCPEKLP8vA,634 +babel/locale-data/nd.dat,sha256=ITmP8mLDb63INBKlxe3yCZqRbvL83DrVBkIIAA_YjmA,15730 +babel/locale-data/nd_ZW.dat,sha256=KpZHrPr_0Ru0t9ojSK9ZiqGZ6-rSpvA7cX9C483ocRk,635 +babel/locale-data/nds.dat,sha256=q3dxEsEcdLTOigPQxhEXuaA4U03umMzIBNC4NkFvino,47839 +babel/locale-data/nds_DE.dat,sha256=Qmh5DjX65W58jXXXX3pxfxS_HVTJVavMspBPdph9sfc,654 +babel/locale-data/nds_NL.dat,sha256=E_EO3ylaBjJNThr211Yzefh5DRbkEFFVpbBMXIQ4U1U,654 +babel/locale-data/ne.dat,sha256=5Ob8IUrudfJp6yCzhHkmYC2mkVY1sd10I79pSXzups0,210152 +babel/locale-data/ne_IN.dat,sha256=SFloADZtyb42wygQmrMy7yx-aADGQECZJUey796oqnw,1292 +babel/locale-data/ne_NP.dat,sha256=LGc7Z5TRi0HRNQdY4bqPitV73gzTPWLd_g-6aPyvryc,635 +babel/locale-data/nl.dat,sha256=zO55PQW5wWMzjRmUGEi5l4H1D1rC90NQt262jbRIyzY,150031 +babel/locale-data/nl_AW.dat,sha256=HYycPPe5HDhuP_zN89ysXR2AqjIbx_-XB2n0-TUPhxw,638 +babel/locale-data/nl_BE.dat,sha256=xHmyTOK9-rwPAH6fYdlPFZv-xinmTFNRN-LIGxb59HE,1876 +babel/locale-data/nl_BQ.dat,sha256=lZQzrmWsFTpiWwT9l6FakrNoll3QNP3drIMcxFkAy6Y,635 +babel/locale-data/nl_CW.dat,sha256=doEeLSsQVaSocP4jr5LxNrT5luSiG4hkgsMvoYjJBtY,638 +babel/locale-data/nl_NL.dat,sha256=bGo4nyjhqe1VtDHlP5EUmNkBKsViUSiH3h_ikXcPuRo,653 +babel/locale-data/nl_SR.dat,sha256=d-YXA5QA5b2Bh-WhxNTkyKQ4ROhuEg1CeGN5XmzWcZM,696 +babel/locale-data/nl_SX.dat,sha256=ciTQIntv0njAJGIUENzo6stN8yarfh-eBpmEzxmpJbs,638 +babel/locale-data/nmg.dat,sha256=NEWUb0RI43oeWByH9l1FvrLDvVQY6501jviEYmM_CCs,15468 +babel/locale-data/nmg_CM.dat,sha256=0A-cVUgt1l1ITnPRdcgWiQ68ZGqHDYd-k2wSc_nXnMQ,636 +babel/locale-data/nn.dat,sha256=E-Y0lC-8QKCXdc8CC_ZPN3bstsXsM3MD4Lu62XMMq3U,64042 +babel/locale-data/nn_NO.dat,sha256=98eWK3CBq5T_dI4a6dQH2TktRygMH0EnpO1EO7w8lVY,653 +babel/locale-data/nnh.dat,sha256=cWTTqJEXln6pUYON7H4PRrYr_uRWktSzcLGCC8jnTno,3530 +babel/locale-data/nnh_CM.dat,sha256=dt9ZNHX5ZQo-kcyT4-mVRCUiuzL-H2A9e0s0JrHwZts,636 +babel/locale-data/no.dat,sha256=xxkSV9bwDKWKK6dmGIaZGo4snfkON9eJUp3-uvlEC1w,191212 +babel/locale-data/nqo.dat,sha256=VQzcAnkUnAETAL8xosXBsQltlC4yqpSZNMkwtsWPRgI,109412 +babel/locale-data/nqo_GN.dat,sha256=3eOSTBaZM0RNLUDZFr__KTR_9iZoGUFq9M6bhtmHjcU,617 +babel/locale-data/nr.dat,sha256=ZHIIC8BaR5XBmnULOTfmAYzXmqImq5QmELqbFPKKQJo,2167 +babel/locale-data/nr_ZA.dat,sha256=e4_czqcyMye0qthJd2hzn_9inTS8WjjbFOzRJ3wAS24,635 +babel/locale-data/nso.dat,sha256=dsq0mvwg5QK35al0bVwZGUL961EuGS05vlFR5qb4HwM,6470 +babel/locale-data/nso_ZA.dat,sha256=TdOqVTzKNigLD6zOPR7W7QHT-Aw40ylsUE7bmMwgaKY,636 +babel/locale-data/nus.dat,sha256=4G1Wwsq1yWIew4nA-QBo2j8LmbqBTRZ0gCU0LYLUSUk,8215 +babel/locale-data/nus_SS.dat,sha256=Is_mWvnSS7j05Vk9RHcCfyLOdhOeOWRqgXDf8FGNEJQ,617 +babel/locale-data/nv.dat,sha256=7CBom-BDX6Wk2o878hmZ8DBY8QAS8Z99ExdmdVpq-vQ,721 +babel/locale-data/nv_US.dat,sha256=ak7j2GDaOMbAAmXQhfC9PRM8E9h_hJZL3UDqxqjCtoI,653 +babel/locale-data/ny.dat,sha256=XXvyFcRnNcH9KcAH1K7jzS5xLFsXMlXVW3yowhXPIO0,2152 +babel/locale-data/ny_MW.dat,sha256=kyhkcpqgIwSrUcfxve2OS2P9S1AfXItRHmy94K7cQ48,616 +babel/locale-data/nyn.dat,sha256=nVlbaYbL0QCkpVvX2rYYUFRsuI8BOjcReljkcamobSY,15668 +babel/locale-data/nyn_UG.dat,sha256=fEm-RMI6-PvIjbMUoZK68dTaiAmOqH1iDQBn23a6r2w,640 +babel/locale-data/oc.dat,sha256=0GZZe142JNEQ78GP7q1Sq7ZolzkVuvuK-QDCpLu4Z6Y,66841 +babel/locale-data/oc_ES.dat,sha256=H-3UJZkJCacW_BELiE_wXsXSVHp-dfxv59o3Hed8uho,33273 +babel/locale-data/oc_FR.dat,sha256=L80Ivl6dUpQJF4fAY1xaAeCRO9BkN0l6oCr8nRwogsM,653 +babel/locale-data/om.dat,sha256=wbIQO2bCc4PnuCB14zg9Gw9XW3mlv8D_D0C39SMnAnw,67446 +babel/locale-data/om_ET.dat,sha256=v0wHN7iwcuJN-iFgWFPrGdplH6P-0c8B2OEqYOEeb40,635 +babel/locale-data/om_KE.dat,sha256=DZ-2HywpH9NGEronFox-2AprzyoSg0xJAS62LNqZxcQ,1365 +babel/locale-data/or.dat,sha256=bdzcLCC-BRprtZBlm8pvQhvGKe2zTqjQM1JPQZrGapM,226771 +babel/locale-data/or_IN.dat,sha256=ur6a2cCdapMoKd-6KLXR2xAF2YU8xaueeW7UTPL2yLU,658 +babel/locale-data/os.dat,sha256=2ARxbjnZ_WYF6VQTsvXN4epcXlWpkXaVEhUNOJpAv8Q,14821 +babel/locale-data/os_GE.dat,sha256=8NgIdMuGlNow1dXmjW6y4xOis1_3raiXcj_jLBPirpI,635 +babel/locale-data/os_RU.dat,sha256=76MsfCZxiy_RXfhW7Do3Z8q4-jhp1hOXTBIsA6SlA-Q,695 +babel/locale-data/osa.dat,sha256=Ob-cu1978hig_-zOnB-PJW1_496aZ_yT6S7VTFkHoEY,4538 +babel/locale-data/osa_US.dat,sha256=qFfjNw8tVOdprzqK2wBlGH4eNMxDxDQqDDReucJZR-E,654 +babel/locale-data/pa.dat,sha256=pMTu_1agUQqVCaM7VB5fe9wM45MEsfVkDyzkKp6UlUE,202684 +babel/locale-data/pa_Arab.dat,sha256=VBfuWzoA5UdGbWTTG3cPAYL0ApWSup5U9LWBBh4bGUE,3789 +babel/locale-data/pa_Arab_PK.dat,sha256=1Si67Mz8DZfo2D8Ld0b5IlZpvnSMgg4W8_zZLrHrWQ0,635 +babel/locale-data/pa_Guru.dat,sha256=fs7wqdmuGaKQCCwGkT3lRQSKmf1dxG3jyLboZeVtPmE,1276 +babel/locale-data/pa_Guru_IN.dat,sha256=kpz873B1I65nfS3Z5vDLmmGliFVPlPM58sB6jW9nkBY,658 +babel/locale-data/pap.dat,sha256=EzSKg0e-PaX4UNpqi0JTYSjLYwqljGKI-uJLjR1uWhQ,28121 +babel/locale-data/pap_AW.dat,sha256=XZSGzQPijHNf5GjxUjDJQt88iD0ETRdZSTF8gn-1U4M,617 +babel/locale-data/pap_CW.dat,sha256=Mu-gCfh3bv9pzz9RrylMffuphhqgiRaODEZm6TtCpaI,617 +babel/locale-data/pcm.dat,sha256=BPyAoOzA7PpWZ4G9w2NMBzu1JqtXwnZbeCSrLk7j-Qs,161833 +babel/locale-data/pcm_NG.dat,sha256=3gBp38NIuVEbHlKAtQPQXJjT3DKvy7zvUqMAZS6pGzk,617 +babel/locale-data/pis.dat,sha256=aCXK39YyoYqy97wCU2idxYXLL9cC7myCIiJZeKgq-po,1510 +babel/locale-data/pis_SB.dat,sha256=-CDkXv_877c__o56bBeYHAAq2X9CcnKdI65DiE61_M0,617 +babel/locale-data/pl.dat,sha256=zWkc9nD_Ipzoj-nuymRuCLdWD3dG_wQicGQE5NpjSLU,227506 +babel/locale-data/pl_PL.dat,sha256=3s2XpVyaKha_K9MesXKD6sZNGSn1PVRtuz9XYp1xrsg,653 +babel/locale-data/prg.dat,sha256=vsad78h5dkCtaL_Gc-AGmXy3cc6Od1j4WCPGBdX6Tt0,17267 +babel/locale-data/prg_PL.dat,sha256=5jT2Yeek_e_MpGShmy2v-0WWhzQkQ0WsJQlUYfIOcfU,654 +babel/locale-data/ps.dat,sha256=2Yvun7eRlWEkh6Om_QxdW3Hq3AwyYqoM8n0418VjcbU,158607 +babel/locale-data/ps_AF.dat,sha256=sNE6bgx_nV0VWKa8InelUaFYSa3Fpx6byJw0KT4B1o4,678 +babel/locale-data/ps_PK.dat,sha256=ZtzTnH2thc6-J5PbuMOq3lPu57kbqDFxO53s7BSdVSQ,7119 +babel/locale-data/pt.dat,sha256=6Db_e_tQGLJop802pMj8aeNGcuKxa9iKTCZYBNM4crs,183448 +babel/locale-data/pt_AO.dat,sha256=49hMN9AJBDNjcsQw-nl-jY_VKHanO0PGfvd--OxqOxI,1022 +babel/locale-data/pt_BR.dat,sha256=dVKYp9IwRMbjZ_ee-eXFPudSuTRrROvbFzY3dYTYmn0,635 +babel/locale-data/pt_CH.dat,sha256=zAvD0sYytEquwzY8ZRbVCcy7DS2ZzUXofl5qXOixZH4,653 +babel/locale-data/pt_CV.dat,sha256=zOQn8d7--PKrhRUhQ5mPKW1Km6Tyh5JF8hstfxpITzM,1039 +babel/locale-data/pt_GQ.dat,sha256=qI1hvtuSHOev12ips9o3Vy78NJpOMjhkW625M60lqZU,616 +babel/locale-data/pt_GW.dat,sha256=SRxbFDIg9FwEjGjutRI9P1SeLiZBqStwSqPuHBFZwLQ,1002 +babel/locale-data/pt_LU.dat,sha256=z9aq0BTWEnk4lXveNpmi89TKcXN_t6Ud4A_C97uyOuw,672 +babel/locale-data/pt_MO.dat,sha256=shItJIIDrxwlcQhWLhlZG0uhyTlSBDcTag5ivxmb9TA,1635 +babel/locale-data/pt_MZ.dat,sha256=UZ1nkXS9CpiCM1IpOG5XpGK2QqM5mE5lAgRLTGAWl-4,1042 +babel/locale-data/pt_PT.dat,sha256=_H5DG5pOSZR_5gjjycrMBKaOJd_euwJln1pa3MPd1Mg,96277 +babel/locale-data/pt_ST.dat,sha256=Cm2HUxlGtZaz8IXX5Ohms-HYuKgazuBcOZBz5rWOzEU,1022 +babel/locale-data/pt_TL.dat,sha256=CIIDM_Zo0xiu4gXngxYtkYK6ldTNZMRaikSD_sAokMw,1002 +babel/locale-data/qu.dat,sha256=UueUbNLnx5ZwsqJcvzKUTdK5_wKBOmR2haCj0zhd4RQ,93300 +babel/locale-data/qu_BO.dat,sha256=7RUvNrmuLtwqP-qab667GocH_c4gIvpas3dbN1uaGxU,878 +babel/locale-data/qu_EC.dat,sha256=_5V4RYoPb3eCAA7jOCNfB7WZqd9sWP1E0tlF8mGeqFc,837 +babel/locale-data/qu_PE.dat,sha256=Vdd0fcK2dtFgyui3Q9NElHuJ841RI-T5l2UHeBq8MQ8,635 +babel/locale-data/quc.dat,sha256=dIAxk2IuDZhWTj-NKC9PiHBZOnG9ARyANdJZUfoVlJw,775 +babel/locale-data/quc_GT.dat,sha256=8M8KtmnsJiTCaf-Nl5X174Omtyc6a0ri07kysDC3-5Y,636 +babel/locale-data/raj.dat,sha256=qkuyRruXTTQLDgijSA0Equfz19Aqg0p5Mg0heaGKT00,2428 +babel/locale-data/raj_IN.dat,sha256=SS8GPzJu5j6nfXlDoX4NFPN2QScl4M0YNsrFmWklymM,659 +babel/locale-data/rhg.dat,sha256=dHahMWjQ_tUshtlLauqf9b4Y36LNJFGrMBsVmjBzlqQ,4611 +babel/locale-data/rhg_Rohg.dat,sha256=DAOuwC-7uaIuZtrIXvUkYt6YkxqaKpHOma1m0-XTtrI,693 +babel/locale-data/rhg_Rohg_BD.dat,sha256=DcLSBtf_k_f6B102-vbhBOjfzNZjiTDvEUdIOQfVc8M,1212 +babel/locale-data/rhg_Rohg_MM.dat,sha256=HspZzbytcZQ853Jstp-eFEdqSMAD3-shgjrdxY_KQrE,636 +babel/locale-data/rif.dat,sha256=4KtUTy4D-OsLftEaIHU2fw1NUhx0pwJXXie51jI3X10,49902 +babel/locale-data/rif_MA.dat,sha256=Qq7ZdymuPdjHeYDUDGCjMuQBRU7w5qGdgzzZ6wPfN2o,617 +babel/locale-data/rm.dat,sha256=aQ4MmwhjJbBakV6-C-6yjDvN8jvVa8Mk0ovdYgVkR9k,95006 +babel/locale-data/rm_CH.dat,sha256=JHAlYXMD9Vks43cU_gcSEQmqo6pogWPl_R4VMUK2iHI,653 +babel/locale-data/rn.dat,sha256=LwQTzbFINNeIhU5qJsEnh-raxcCDXVMGA6ZL95aU9UE,16241 +babel/locale-data/rn_BI.dat,sha256=r5Cweh7L4EAZK-qTmJuloWc33iXeR4IJUbknrl0XC3g,616 +babel/locale-data/ro.dat,sha256=13UpR66j-nQg4kbgGH4ja6ygDq1k3CCErykEWeeG8yY,200714 +babel/locale-data/ro_MD.dat,sha256=pUZY970zkBSBpVOIA3Ss-6vDJUF3ZCEWYNs4_LXcpAY,2881 +babel/locale-data/ro_RO.dat,sha256=lLYIYWhDUmq1EZ9oV5SETauVa_5uNN5p52dW7XeemRk,635 +babel/locale-data/rof.dat,sha256=Yafgax503CHjHvrc_njDTMVOOD2B36sRvWvDE99geMI,15473 +babel/locale-data/rof_TZ.dat,sha256=I_qwGjqqrcwUR_3UNydHOm_EiiLZYyRV0q-s1qfSSi0,617 +babel/locale-data/root.dat,sha256=vcYJrDi6WVier8432VEhdQ3ZZH8ptAXszNP747aIiq0,51983 +babel/locale-data/ru.dat,sha256=8WD_7_Tz0ieaEjXNlFQy8xFSfCwsVf_CUcoHyFfqkgs,305832 +babel/locale-data/ru_BY.dat,sha256=99UlyhoMWnfyhI5ltXGaJkycWLVfJ3FwdxyRm-yr6kM,676 +babel/locale-data/ru_KG.dat,sha256=X-iAT1QuQGhk1mIetpGyWqDlwiBskLfwqrK6ant1XS8,659 +babel/locale-data/ru_KZ.dat,sha256=P1_Gn3esOwDLL8sd8vVYbFA8J7QEsaDAWfZvDsn-WwA,656 +babel/locale-data/ru_MD.dat,sha256=LU12oBwr7MfPTzfs8Dx7uUZVgHin8ybX_oPHrPIJTkw,654 +babel/locale-data/ru_RU.dat,sha256=IVG440mXjphW6iCsKysZCW1Gem_z5_O0lZI6-R3oG_E,653 +babel/locale-data/ru_UA.dat,sha256=WYPDEOUjHl7mtejs302KAURq_ckDn6CFjH-IwmiFF6g,1210 +babel/locale-data/rw.dat,sha256=1KiBkw5N7hioc-ESs0oqgdyOaVdOlsNp1iIz7g3k83E,8418 +babel/locale-data/rw_RW.dat,sha256=tw-gtzH64PeIAmNGeobgUypw7RAyKITeFAcQPGY0MG8,616 +babel/locale-data/rwk.dat,sha256=hCqo5glP1WqFJrEGyW6r3t43NZ7bVHAOGFGDkUzeM3Y,15369 +babel/locale-data/rwk_TZ.dat,sha256=s9duai8NRoHwn8YpQrlMd3VDf6jtQxxcUmu9CIIMzC0,617 +babel/locale-data/sa.dat,sha256=8r4iZXIQ3vnGxlSmOtDhwM-T_iaYN4SXuTSdGh-2JMg,15978 +babel/locale-data/sa_IN.dat,sha256=ABNamoPpYRbO7bK6BGm0tK92kd7su7_OP7Di0wW0zZo,658 +babel/locale-data/sah.dat,sha256=Qlb3HEv5JslfLAkUjLJz_XdRdyLhloQ9uf9q2b_O6jM,39310 +babel/locale-data/sah_RU.dat,sha256=jHlseiwMbtN-IsETeG2dsaIn6OB2kqtAogazMnn0f60,654 +babel/locale-data/saq.dat,sha256=grj85np3tZzXQmLdtYQcu76ynQv5Jq__KIyaDECFIj0,15787 +babel/locale-data/saq_KE.dat,sha256=kN94cJJt8AT0yBRDqUPGoJtMZdOb2e-83NNsq8hmQ64,636 +babel/locale-data/sat.dat,sha256=K_IKB3mSsOkYuYDtbGW3lbGjVk4ctB56WWkJJvE0-mI,66131 +babel/locale-data/sat_Deva.dat,sha256=0EHZ6UcxsVOfi399xQbOkzaH1OU4jkZ10uqmmWnK8VI,1943 +babel/locale-data/sat_Deva_IN.dat,sha256=Bzh9Vmzia_8KLAryt9VqCl9ter9tKxng4b8oFYbYK6I,659 +babel/locale-data/sat_Olck.dat,sha256=mszOmf6B29mFbE6qN6Moy6SfBjwsjaCsbk75dJR245Q,905 +babel/locale-data/sat_Olck_IN.dat,sha256=Bzh9Vmzia_8KLAryt9VqCl9ter9tKxng4b8oFYbYK6I,659 +babel/locale-data/sbp.dat,sha256=1qHxon8_TpGGnLHUBzy5cR1mCEnZmw1EuYz3QLgnT-c,15609 +babel/locale-data/sbp_TZ.dat,sha256=chWs0tZniO53SbtBquTHGS6E4v36tgYL2b3enxHcdCU,617 +babel/locale-data/sc.dat,sha256=ItaljMMLxOEM4DEG_yDNVk8H4AmQrq1xCnDE8AfRDDs,189970 +babel/locale-data/sc_IT.dat,sha256=0G0vL9YXZQLkTmnzJuuygM5ty2Tw5liDg_aWHLvg32k,653 +babel/locale-data/scn.dat,sha256=UKMqekdt_t_Qe_ebhg3eaRsTs37ECpWLEiWSCFjMTLI,70373 +babel/locale-data/scn_IT.dat,sha256=dTXZIHDRhbFDCw02T0wL1iaJ2kfY1ZUzfbvUs4_WKLs,654 +babel/locale-data/sd.dat,sha256=VDMIluqNpBChcALvjd2lGll6sRK6srAF7p2CogEgXlE,154300 +babel/locale-data/sd_Arab.dat,sha256=jgkaYGUhPXll1T7CT68ridccZaERHeZdwUzGoAZBUps,879 +babel/locale-data/sd_Arab_PK.dat,sha256=c3AXdatnrTNDHW_SOh8fNNye4CVey81FnkSXwj7MmcI,635 +babel/locale-data/sd_Deva.dat,sha256=ZZathBrGJyoqspPsHF769J84zQq1y62vN8RAjGj_Cms,13957 +babel/locale-data/sd_Deva_IN.dat,sha256=0KQFEKHV6hHidc5hxrxQEvhO-YyvFf5P5z6t9hPenH4,658 +babel/locale-data/sdh.dat,sha256=5cD8u88qanpCtA8kPvHH9AdAi2-HbhznORqis1PYx6M,1032 +babel/locale-data/sdh_IQ.dat,sha256=B_vUMOiGWVcD1HaR4Zk8XxVzcmMFSISe2vRscyA1lYY,679 +babel/locale-data/sdh_IR.dat,sha256=hwMmQnne41X_lyjbyNbQq34Lpkl91bAgoavPZRuozwU,679 +babel/locale-data/se.dat,sha256=JnN5s3b1OrcP7qmurb8Rb9NKc-ICOr_RmdFs2EBKoRA,54830 +babel/locale-data/se_FI.dat,sha256=9_VJ0wzYFPto0YF2Ge0qieFV9_DOVqqYnPjlC5wnAr8,44207 +babel/locale-data/se_NO.dat,sha256=nm5u2VQ3ne0SUFZtFqwsRa8Gg97Xn1OSxjoP2I-i_PU,653 +babel/locale-data/se_SE.dat,sha256=6M9OxtmQAY_p9fZ_6L8zqo6i--bo_X00cvhTDEbX_UU,694 +babel/locale-data/seh.dat,sha256=viFuK-Mg4wPnvTcrvL-iEzNqFc7Ri_VP_r9pC673SNE,15433 +babel/locale-data/seh_MZ.dat,sha256=Sv94DueqrM3R5RjhCdwrKZsnNdL1WpjLBg6QhBnoaX0,636 +babel/locale-data/ses.dat,sha256=aRW4cH7nGNS5S3B3ff7o02GqcIqn1mVbBRGg3NdxXUc,15812 +babel/locale-data/ses_ML.dat,sha256=y14qGwP_r6VxzJhklYIWfYkolu0OHB-6nJ4tvF7J-iU,617 +babel/locale-data/sg.dat,sha256=ERl6d_xd8JRnkG_D857gWi2xONyI-cEmzt-Un_hXbu4,16476 +babel/locale-data/sg_CF.dat,sha256=QCNYqo_5l2Sv9JUFEWF6YLDR3MknhgkyMz1mO5j2Gv8,616 +babel/locale-data/shi.dat,sha256=fuYtv_xjgmJ0-aAQiuWcJXZY3mpeq__hz4lnouEUyWE,21830 +babel/locale-data/shi_Latn.dat,sha256=aQrfwwcvkgPaWFSev9-3WgwQXhhXj1_5myuR-QifnPo,15435 +babel/locale-data/shi_Latn_MA.dat,sha256=X8cwkkvm2LjcClQWzHRxxmiA8P0c37D7P8lf6RO4iNQ,617 +babel/locale-data/shi_Tfng.dat,sha256=VjsR4oSiK9oFfdIdpaVDwNSIBvFbQ3Jvm63K24bRqk0,974 +babel/locale-data/shi_Tfng_MA.dat,sha256=X8cwkkvm2LjcClQWzHRxxmiA8P0c37D7P8lf6RO4iNQ,617 +babel/locale-data/shn.dat,sha256=awBFmgdlHbpKsnUqWZaGAQ-YbTPm9UWkJKMn9IdJ7pM,890 +babel/locale-data/shn_MM.dat,sha256=7VauV3cmVfAJz7kA76hsF3FdwM75CVjvHdjx6lCoH4M,636 +babel/locale-data/shn_TH.dat,sha256=QHPmr3V4GzTbb6QA5SDl2EsVzQBjpbaDP6OHfJZ0S0Y,636 +babel/locale-data/si.dat,sha256=iuq-0rbecyzhX0pRX5UYdgKKzrn09pkLucKursW_Z94,216775 +babel/locale-data/si_LK.dat,sha256=cWCgV9WmnEwIIsBmhsIVzSzbrV8hkkXaV1xH1ultlUw,635 +babel/locale-data/sid.dat,sha256=zyKwuzw51B0E8sWg60qJ3kO4rzqTtcNBRnH5D3Zm1mU,2176 +babel/locale-data/sid_ET.dat,sha256=vtwy9BuaZwb5A3XdIl-3mws1cEAVk4s8oKy7i-GnM9o,636 +babel/locale-data/sk.dat,sha256=u_TnuG4-zvLxkxQupdNvPS-TYi28xeiPxLit28hxwZM,218242 +babel/locale-data/sk_SK.dat,sha256=eMjPq18mQOAXvkJN5WV6lKEAPyt7-eleGme2pHHevgA,653 +babel/locale-data/skr.dat,sha256=Mrx5bET3kh5Gzd8-2J9Rm4_b_wPZTDoHuhcxqdYj5hA,1702 +babel/locale-data/skr_PK.dat,sha256=UJpLMmJJIM5jeI-tXWPBNX4Qd3WS2nTwp3bvJg5D3WY,636 +babel/locale-data/sl.dat,sha256=eEJU8du-WwCkCviFmusUqH8H6sMl2PHG_khSumUB3co,210137 +babel/locale-data/sl_SI.dat,sha256=3Hk2t-MUfAUtqu_Gy0_fTjOhwBUtxdnndQ7Kowg0qek,635 +babel/locale-data/sma.dat,sha256=BaBVbhsEPN9V4JGtMt2TpMdf0oIrL8Maxt2FXDIEEss,944 +babel/locale-data/sma_NO.dat,sha256=OeIJ-EJtvWH6z2dZ1Hv8QdmQC7M7r51t-_C6LD6ZyxE,654 +babel/locale-data/sma_SE.dat,sha256=M_zYllAUd2UaWvccZ6ShB9-Gd8JYzIEln-88Q23syaU,654 +babel/locale-data/smj.dat,sha256=eyHR5AZqbxxlTYhyxJfvTjpz_v6zfh3TrDtcDkCgTig,939 +babel/locale-data/smj_NO.dat,sha256=5fz-YVJEoXswvC-RdFcJWmt-EMGcNXA3c9j_pfmxqCA,654 +babel/locale-data/smj_SE.dat,sha256=7l73FEZcAemOwXSrfPB3X7a0cto3J4oM7AO9i9PMIBU,654 +babel/locale-data/smn.dat,sha256=LGkyaifqt24L0NeExCtzhg8rKEKnkfJNEKtt67B-ViE,40399 +babel/locale-data/smn_FI.dat,sha256=VRY3dbCyxEmDUTep9bQXDRldd5wSg4skqQpztnMXplk,654 +babel/locale-data/sms.dat,sha256=ylATYbrfu8SVEVdQRbC7dTSOKCNSX7O1gjS2wJXA1tU,6521 +babel/locale-data/sms_FI.dat,sha256=b-Mk20lC32xysqU5v6jGjDUYDZaMDvoMwWOZNdT5yuA,654 +babel/locale-data/sn.dat,sha256=FNTHDspBYVPY7Nse1haEc19O0Sf3btsqNWQFs2Zzcgc,16732 +babel/locale-data/sn_ZW.dat,sha256=8HnH2raHHl8PI25abODlThEhjIOGXJb_u7cyQd5YzZw,635 +babel/locale-data/so.dat,sha256=sAFkgMg-qaZYhHT_lyoaggZ8UM_KX1xbS4miB3l5KN8,166227 +babel/locale-data/so_DJ.dat,sha256=VWnaj_MuW4dJnlO84t60ySCvfJL7FXI4nIP9PTL5Amo,656 +babel/locale-data/so_ET.dat,sha256=EvRRqU4yQPMYhkEK8a4MR808fwKHmW7EvPvEJdjltnE,655 +babel/locale-data/so_KE.dat,sha256=OSiRggRZ7CTNmqiGOvW1W2Cw2RqjVESTVOdUfgVgWyU,1208 +babel/locale-data/so_SO.dat,sha256=2IPbOqSxQGB5FukQ-B-MEme9FHYn22ClXmAV4x08pNE,616 +babel/locale-data/sq.dat,sha256=8lTejyAm-94ixpS6Dly7NQSHNsamftqLIdO4OxPWqok,160229 +babel/locale-data/sq_AL.dat,sha256=MN2RvkKGG2PXjCRsMHsvcnwnIOOp9tNSpcInh2uCWaI,635 +babel/locale-data/sq_MK.dat,sha256=L_GaZJ9XuQMs0zw-xZlrefPF36SH0sJqMrdDDbwicZM,1208 +babel/locale-data/sq_XK.dat,sha256=qILZGRwAhuIBJ9xi_T5WTrrZAVouFjQSvybrwgXTsog,1187 +babel/locale-data/sr.dat,sha256=AAE3LN_CLRWzljEERIYedZepxLIDWzENkL4wmu8Ib6c,265725 +babel/locale-data/sr_Cyrl.dat,sha256=Tbo90CyBAWciKeDesPhmXf7sdBQfjWsmyyrKBco_NzU,1990 +babel/locale-data/sr_Cyrl_BA.dat,sha256=sKEcBX9G9OwCmIZEpTphQyW_LM4xoDGe90s_470_AJs,41992 +babel/locale-data/sr_Cyrl_ME.dat,sha256=rDZOf-zykN151tfgGiO1SUJiClzQ6t40gF927bMLn1I,2320 +babel/locale-data/sr_Cyrl_RS.dat,sha256=Tn9u1EvJAZZEIdgXloi326qjpq8b1N087nRQ9R1JbWE,635 +babel/locale-data/sr_Cyrl_XK.dat,sha256=qVevTILYnuaIjRyPy4rDTavVg-VXxKcZ5fVdh5c-aHQ,1574 +babel/locale-data/sr_Latn.dat,sha256=1joKXYRv5UiiT21s_Za6qE-VA8XpFi1HzYnVOjKpxb8,217950 +babel/locale-data/sr_Latn_BA.dat,sha256=of4wpeCglVK_Td6HnHUjHRwuFtCVtWj3kHb3nl4B0D8,32834 +babel/locale-data/sr_Latn_ME.dat,sha256=Mr0GNVLFIMZpBakd4dEUzJUlAN4nyX2BMAqydgtMzWo,2089 +babel/locale-data/sr_Latn_RS.dat,sha256=Tn9u1EvJAZZEIdgXloi326qjpq8b1N087nRQ9R1JbWE,635 +babel/locale-data/sr_Latn_XK.dat,sha256=wIz7XXQrRFYpzMpR_cgSWikPWI0ehSx5a0gRBgt3rFo,1486 +babel/locale-data/ss.dat,sha256=UAB0p3UMDn8KfBY32a7HVWXWpHlPa26Nr_Woaah2c3U,2178 +babel/locale-data/ss_SZ.dat,sha256=X-Va1uVrsPa1ZX5hwhQ-ME6Q_bovUb24XXFS8UQ87lQ,1208 +babel/locale-data/ss_ZA.dat,sha256=xwT3vZ4tmKEvJpVwFMV9U2j2FLF1tC9PEDN6MC6Qbf4,635 +babel/locale-data/ssy.dat,sha256=QyBWLhH_sreFEBHMQ7aiqBsrGvDeQHyYAKioqksa2iU,2999 +babel/locale-data/ssy_ER.dat,sha256=9CRIaVxgOMeMTc0tcV0Yz9yZzq_uD3g3ylZzqVpYzsM,617 +babel/locale-data/st.dat,sha256=b-jiK03L_9F4QqWW8qmRJ3UZntZDI9eMJglRho3MN7c,8016 +babel/locale-data/st_LS.dat,sha256=pSzdNm6-O5V6-14VcgF2iGNYQPFYIXQlY3SqUW3GcjA,1227 +babel/locale-data/st_ZA.dat,sha256=5tdUX7dxXEL2xiS_0IvHvqhKF-gWXFsi4supkP0RDAc,635 +babel/locale-data/su.dat,sha256=G66EuouIQrCxgKGW9JzPfMZ94NAN87P6REyULyyrFGw,11817 +babel/locale-data/su_Latn.dat,sha256=ievzq_m7eD5pZWLnVlH7oMIdeadf8GIMqEuVPJTBjyM,745 +babel/locale-data/su_Latn_ID.dat,sha256=axNaukDpD_-G-_4R7P1u5w4dh-ToRPN_Yo-QpC1cFRU,635 +babel/locale-data/sv.dat,sha256=RTvQ2li9cKHIEbVl7fI8ENVrmcCa1gERk3S9O2bHKmA,198967 +babel/locale-data/sv_AX.dat,sha256=QFGzIe9UCr5gCpgHunvdPiZ-126vP7GYORL9jjip9-E,653 +babel/locale-data/sv_FI.dat,sha256=Pn3EDh-VD0QJFWupEZ6w4TqAa-WC5GZ8oTA4oqLazvc,2526 +babel/locale-data/sv_SE.dat,sha256=c9PG5p2y_4rnxepGD4uOzemBU3EOQhQNO4BtUS1m3rE,653 +babel/locale-data/sw.dat,sha256=w0uX0MB-_bZQWOnbdyDVg1uRVe1j-K-egjRExoZpE8U,145419 +babel/locale-data/sw_CD.dat,sha256=W7Fw8Wqwo3_x4hWlssmWbVTj4hhSQmRv8c-VSnQdlg8,2577 +babel/locale-data/sw_KE.dat,sha256=YMNWSTJ8qxo0d1KvQwAN5ECRaZ9LGc0BDfX4enggVlc,47036 +babel/locale-data/sw_TZ.dat,sha256=I1bDbpEDiR1jkCgJ_W9fWCPuPxMhjQmwG3sIueKb-Rk,616 +babel/locale-data/sw_UG.dat,sha256=kuNdg2N_dLwUKFm7wSheL1wLOdyrukPxLfDw0PN7tT8,660 +babel/locale-data/syr.dat,sha256=ehEqE2rAN5_5X-4zoWys-oUwiajRRhwkGx-yd-kL4Q4,111439 +babel/locale-data/syr_IQ.dat,sha256=Mrw63eJwh0vRe3Lpj0vRf6Q4cUZXJcx9tlWkuAmMrss,679 +babel/locale-data/syr_SY.dat,sha256=l0Yl2UMjm_7kuzvoG2-KqBp5XR88gdjf1ZlpRRNFMuc,679 +babel/locale-data/szl.dat,sha256=8PpGSXa6ES6yxXD2YOBjG16b6BW9jDZo86RKB0MCNn8,88759 +babel/locale-data/szl_PL.dat,sha256=1D5dKaG2l2Haxde8uraWI2J5lzw1dIWGeJtBesua-IM,654 +babel/locale-data/ta.dat,sha256=RRIrxhZ9pdpGShIrQx7ZQBOPKWn15OGkeL8yQOy-tz8,277264 +babel/locale-data/ta_IN.dat,sha256=BOFaDBw7fc7C6xSSFYNfkhCgCUd7ydHprJ6u0DaneSE,658 +babel/locale-data/ta_LK.dat,sha256=Od6ln5oUnWfaAHwt_SDafWv844c7A7_7fTT2OPVTuyw,1208 +babel/locale-data/ta_MY.dat,sha256=PEUmjhs_RUr1R5kH9uHExdnG59Hy8LmbU18vXZYYPtA,1325 +babel/locale-data/ta_SG.dat,sha256=tbuefTSiXPMwpeV085Qp53l5uDHLsMMXmt8TPEG2fWY,1344 +babel/locale-data/te.dat,sha256=oRTQlv5dIZYav2aQWijvc2wm0VwoR1TwWUPL_qhzkuU,261668 +babel/locale-data/te_IN.dat,sha256=LICnXU-D0uhvxVpQ986Ul1JeAQktXaX53sXZSUY3L2g,658 +babel/locale-data/teo.dat,sha256=jJ-266Aw_zTeJjwGj_VASqzm6Y3CcQYkVPXKuV_kzT8,16012 +babel/locale-data/teo_KE.dat,sha256=S_lAg36VkA1w384kzPfXRLmJmGWfmz3ll7MzOQAtcr8,657 +babel/locale-data/teo_UG.dat,sha256=0WlDglms5bCe1AMwCqroaXpOXu9jQroxllMwXWLb8q0,640 +babel/locale-data/tg.dat,sha256=ErMv0Y4SGno9gTSJbhMVKZXiSe_KNSUFaoBeIDaP8tk,117163 +babel/locale-data/tg_TJ.dat,sha256=TCrsEipGp0reAS2mAvwT58tyzBnYJfL5oNHWpEe1ttc,635 +babel/locale-data/th.dat,sha256=lyG_l3_QzfpnjCv8LVaRTvpvfImkVrqcDoUKrnXlIR4,215576 +babel/locale-data/th_TH.dat,sha256=_QhA0WcHmW6Cr_-gU9e6cdpzTMDT19WyHas58fWDUnc,635 +babel/locale-data/ti.dat,sha256=K4kS476YQonyQifjLpS4UkE7hNa2k5aIDBJRIdh2eDg,214273 +babel/locale-data/ti_ER.dat,sha256=m9w0P5TPQ8IqpWKldwdfnw5_1Upv09AjLdO5WUbcRic,962 +babel/locale-data/ti_ET.dat,sha256=q0b9svPg8aj9xmbXr7F6fPHqeTJmclBl5t_hqQk7IuU,635 +babel/locale-data/tig.dat,sha256=cgqxy3wiehP8yceqa5-mrJVT23IP_bjlkeUOEvxspUM,13481 +babel/locale-data/tig_ER.dat,sha256=OoQ0U6yyzmjKwDtzLUpqR09h_ZaGvzXlp7t-HqNxmm4,617 +babel/locale-data/tk.dat,sha256=z5i4kshTasDEolkNpqXnzlZh75EgbAhL0ylT0_0oioo,164932 +babel/locale-data/tk_TM.dat,sha256=lADxEQoz-_ImDBmpCFUqDPuZ0ozVUhZKOQF2ZwyXVlw,635 +babel/locale-data/tn.dat,sha256=LgoZgcUCS4t_RsyQQKiVsJ7Kbs7HoInZAMndRk6DUCM,8596 +babel/locale-data/tn_BW.dat,sha256=jWMFP1tw-o7rslHAlAIgjEXflko8UCDNaSuQ8ccvNHI,654 +babel/locale-data/tn_ZA.dat,sha256=S3dMOsYSpBwkUv0tW9q0kKawQQl6yx77LexAL5wQqDU,635 +babel/locale-data/to.dat,sha256=CBr6gRgu0qo_-kIoOluPwu_ZdH4-SPPJHto_ERcDzHA,145255 +babel/locale-data/to_TO.dat,sha256=jcfJ2Pjof8jGE4q1y5LHSP9rb4IanN58kR7OgG8d5Q0,616 +babel/locale-data/tok.dat,sha256=kuSPgVhnv8Rr55G-epQDVyh8FfeKxJbd2J0hierJdUQ,8870 +babel/locale-data/tok_001.dat,sha256=PPUMevT2TBOGFSq-U-AccYkLHSbkWnBRDvxtpokTCD4,693 +babel/locale-data/tpi.dat,sha256=mqTAJweAOy1SAH2l4mJAKktX-ZeAdvTC8JnBPxXjB0A,4030 +babel/locale-data/tpi_PG.dat,sha256=uZD_c8yCtiYwt2EO2VOYPzhSTqWMQBOJMCuMCXe3Css,617 +babel/locale-data/tr.dat,sha256=ULbjOvmQqcT1Z2_8bBOe7LzwgBUg3LWM2MiLkmb8M6w,150824 +babel/locale-data/tr_CY.dat,sha256=3EmCqxcPvFuZcHCzpTmHb6pjtPwqQvwCfn27B8rtXNw,1227 +babel/locale-data/tr_TR.dat,sha256=7lS5CMZw76SVlMJxsmIJW5IfL7k9t8MrfKcnElqISSI,635 +babel/locale-data/trv.dat,sha256=iEbFtCCE_U5rmjAv0Pe5J_UigixMKq1cPIThdZui1Lg,9404 +babel/locale-data/trv_TW.dat,sha256=CTQtGdDW4Z0n2Ckb6B63HgEwNE44YTFnJCetwd8q9GU,636 +babel/locale-data/trw.dat,sha256=1e1LGCtVq79RclEMJbgRE4ATdqflV_KDHxBwInSfAKc,110107 +babel/locale-data/trw_PK.dat,sha256=pBHdOulrwRLiQuC96g8ilUzpKBeG6kglBqUXB8MlURA,636 +babel/locale-data/ts.dat,sha256=p7EFPucK9HkTVXeNKTU00s-Oas5U_HCPGX9hqT15p9I,5522 +babel/locale-data/ts_ZA.dat,sha256=AByEO55W3DVVOBIBxIp1F2QwY01-5v6v8sDFOGSxCF0,635 +babel/locale-data/tt.dat,sha256=X-J0BKEHy01aoARwwb0SWDuNm1lZFrT0akKrzBZjHPM,111014 +babel/locale-data/tt_RU.dat,sha256=cmv2MzolKOrsRoqQcFZGcXQj_33dLWGh9oUjnebCnz0,653 +babel/locale-data/twq.dat,sha256=nOSN5UZAxcCgdTxQFh14Cyk5K38olCyAuT6MrVW5PeE,15509 +babel/locale-data/twq_NE.dat,sha256=y8xQDxMtyStWC83tm_JiekFxAY0Fj-CWWdmic9GBwd4,617 +babel/locale-data/tyv.dat,sha256=y90_CH7Yksd1Kuc6KhykhpkdGVLUgFDV-B4oSxGqn5E,693 +babel/locale-data/tyv_RU.dat,sha256=vP_6cjMLB8I8YL7Nqrq4Gu_AWGFuANbZSdBc7ORn_IA,654 +babel/locale-data/tzm.dat,sha256=wYFO2vX38bht1nAdcVA7dxKVMh-3yC5sItggFaXkKBo,15436 +babel/locale-data/tzm_MA.dat,sha256=8eku_7voAqAjN651VYKCuIWvdYGOScCyqbt1rtSVO_g,617 +babel/locale-data/ug.dat,sha256=jRJJxRRKuYljj6kYicIhxWk8ySzpMT9AOePaBk3EO94,117801 +babel/locale-data/ug_CN.dat,sha256=2UselU1_d6K7NgQj0EDnDfb4Llk51eQZ7z78oYE-Ywg,635 +babel/locale-data/uk.dat,sha256=b8iEhvLdQxq7rtlqpx1c9vp6-n3jNGPGSuBWNhZAnFA,339125 +babel/locale-data/uk_UA.dat,sha256=usJw_J4Uu9OD9nrUxNQAx6vMJBWDjbGuc9l7ssJAyuw,635 +babel/locale-data/ur.dat,sha256=RTL85URWDMA3albZQODUCbVTA9oxeYXo5kWtuL1XW6g,167002 +babel/locale-data/ur_IN.dat,sha256=9GV9YIa7Mftq14Po2C6fjMG5sWkU8J5LS5bq1iIcsHg,10550 +babel/locale-data/ur_PK.dat,sha256=EEAGl6fLd1-a7X0n2PwRxlqwYZ0a0ELm2DuLII9spX0,635 +babel/locale-data/uz.dat,sha256=iwj3ukxb4FK0rZYtC66ftQmTtr1SgTzy12Wu3KkQYQo,138052 +babel/locale-data/uz_Arab.dat,sha256=wt8BwimSNmoFzrnduGDXMHhqj3B8-XqqN6ghg2Xm74A,3814 +babel/locale-data/uz_Arab_AF.dat,sha256=68eUCR8KeB0QDQRCvNMiOIkZVRozGKFTDednrQL1X_Y,678 +babel/locale-data/uz_Cyrl.dat,sha256=RpmyW2YhTwN_o6sKyfJxcktFI6VMqkO6YDzDE8XoOTE,76581 +babel/locale-data/uz_Cyrl_UZ.dat,sha256=MWJtHPdj_pF2h6abRP4wUwDWAUF4-zr7jBn2RQcZ0z8,635 +babel/locale-data/uz_Latn.dat,sha256=L5H7Xfz2Ho-vMqviRPC4ai0MnqoF9CIebiq7muISvSA,1292 +babel/locale-data/uz_Latn_UZ.dat,sha256=MWJtHPdj_pF2h6abRP4wUwDWAUF4-zr7jBn2RQcZ0z8,635 +babel/locale-data/vai.dat,sha256=T_MWlDAfbp9qgeArFd8yPDPbyThU15kFu19QTQqAlCE,17416 +babel/locale-data/vai_Latn.dat,sha256=MecJYJumVm7wCyCo-YeAY4eAon5WZ4ZqR0shQ3Pdpu8,14255 +babel/locale-data/vai_Latn_LR.dat,sha256=LdVmBAL7nzDp-Ab_uHidBAi2crIf9nXhf8tBNfa7xdI,617 +babel/locale-data/vai_Vaii.dat,sha256=l_IXC76zzJ2lPLfWfjWzSw6xVtFwr-x8LofcHQsSG_0,693 +babel/locale-data/vai_Vaii_LR.dat,sha256=LdVmBAL7nzDp-Ab_uHidBAi2crIf9nXhf8tBNfa7xdI,617 +babel/locale-data/ve.dat,sha256=5bInQioXkePSDbBerQqf4sYPTiB9-zpKuDokCXX5eNA,2372 +babel/locale-data/ve_ZA.dat,sha256=uJndHi9RMKtDWd58f_Q3g5WgXjL3W7KLQj91rVueabQ,635 +babel/locale-data/vec.dat,sha256=zokLfsgTK7cSP4KaSV8EMpPlHiOkI6tG3rWM7o7vcAM,160413 +babel/locale-data/vec_IT.dat,sha256=mC9jRfk9ndmkpRIpWZNCwdMwvxvB6QX6_9HanawdaUw,654 +babel/locale-data/vi.dat,sha256=F95a-fvLPIeJqOlwx4gr9TnWZ0VpSPyzNwJ0RPc1qE8,134138 +babel/locale-data/vi_VN.dat,sha256=cGwN0vw-qIaeA2g89U97N1xHSMmmaIVNPCLL2P_QJMk,635 +babel/locale-data/vmw.dat,sha256=mhERiGM-732X9KO0CThGEyjFtSbquT1_rmzYPnru2SM,1796 +babel/locale-data/vmw_MZ.dat,sha256=sz0j5RufN9oAzTalcjWNhKtCoEXzeRr5Cv6qCouK45E,636 +babel/locale-data/vo.dat,sha256=werwY25uwomUcf9q1dyQ8Rwgg2aCoLp78XUzBeSw0LY,4609 +babel/locale-data/vo_001.dat,sha256=iF2xY9XIdxC0mOYEp9hjYZp2EG-si_YaNYXri_60-4w,850 +babel/locale-data/vun.dat,sha256=VGNPlnoRipnocGYPgeb39d9MmE6fe5nTMWtCk4m1AEY,15373 +babel/locale-data/vun_TZ.dat,sha256=YKrOeOS1VnU3BWQ7HWtxjS-gMKJcKAPQLf4HTgkzf70,617 +babel/locale-data/wa.dat,sha256=1r6krApI8oecIKQt_9RzbpwSN0LcWSjlG34Y9Rfh07Y,880 +babel/locale-data/wa_BE.dat,sha256=j4YADQloo07Oek1j3aOrLYkzcFkrViS1UdRrA0Z56-o,653 +babel/locale-data/wae.dat,sha256=qBKStFqpkAruVDgeZJvX6U0X-hq_NkETTqgR2e7A4UQ,29641 +babel/locale-data/wae_CH.dat,sha256=Z0Wzu5Q9IVTnLGwWu54ozVx-fgnFz0IE3yrky8Y1IkQ,654 +babel/locale-data/wal.dat,sha256=AD9ZfdNNjEhX2n9T0ykd_XwEbXK-wxmsLqq39O4BjWU,8406 +babel/locale-data/wal_ET.dat,sha256=tNjeuU4GPTWtDmsroGmi4VUql0dA57v1tEtDF4rslVg,636 +babel/locale-data/wbp.dat,sha256=kzXK04f538n-PrJl6nRKIaYHMid6rJ4ACmzPcCz7NgU,746 +babel/locale-data/wbp_AU.dat,sha256=BT2E1-WiomuRrteCzN1_AfLyDUdEOm0ehT5miAiepBQ,636 +babel/locale-data/wo.dat,sha256=iB86SoTMrnphiHy_om1Yxs-J8L5YFWYlbmiQjK6jc2k,61849 +babel/locale-data/wo_SN.dat,sha256=fvlwe-ImWs04fsxSjTsXf8IPxEIiEi45BX8xhoBC_yg,616 +babel/locale-data/xh.dat,sha256=xRy095MO_brgAUoDB3UKwhgDvOkBlhJ3cWj9dM2fvHI,64334 +babel/locale-data/xh_ZA.dat,sha256=9p6wHW1sxOghJMAgsP6BC3RdUhuvLpHoCkqvjsCmPps,635 +babel/locale-data/xnr.dat,sha256=Ot10zi4F4H5Yqx6kIJGPay65Rm-Gam2eJ7kAOgoJoJ0,142585 +babel/locale-data/xnr_IN.dat,sha256=UbNwudCNihEFVZu3ymanDhJ52YoupG0SDGwmDByPhzQ,659 +babel/locale-data/xog.dat,sha256=V4U36ibPCWDoUMf31-o9mDCwylsYU0nZEx5pt-MWecM,15866 +babel/locale-data/xog_UG.dat,sha256=bMX0Tk5zS4P2LqlOLLpVP4nZxBFhS_qc-ndXjilDCNI,640 +babel/locale-data/yav.dat,sha256=UupgFFmPVcV-IgY3Fd8Y9GoOcOWgY1P22ZHuA-4QSMU,14543 +babel/locale-data/yav_CM.dat,sha256=CsaKwep5CARetyBX-JXHFvUVsFe1KTN4Pyz1Wh7NTHw,636 +babel/locale-data/yi.dat,sha256=OWr2zb3k_toEmkSXcPweqE13Tr5RhvlMm4z358uCQiY,24264 +babel/locale-data/yi_UA.dat,sha256=DesjWHTuxEBrjOeq8c-B82j4Hnwu0ElR2IH3fZPmq-A,635 +babel/locale-data/yo.dat,sha256=iWLg-08Fk9QJLZPuokgbCeXkyfyQNaov51TT3i1pKgY,110427 +babel/locale-data/yo_BJ.dat,sha256=CP4B5N8cEouMKth-iCTTBEN74dEpdyZQTJbrbYayZFs,50036 +babel/locale-data/yo_NG.dat,sha256=BEIndIiMT1Mo18S5DAWpjefthptCVQkIPeVs0umSop0,616 +babel/locale-data/yrl.dat,sha256=2nwDuw5Q_dL4H2Oxl5ClzZU2Uw10j-koP5RgfnJ67y0,186854 +babel/locale-data/yrl_BR.dat,sha256=aUgLCNwgEORk2wtuez8AqkYahB8_mX41IWSqogt39YE,636 +babel/locale-data/yrl_CO.dat,sha256=pT0rllDKHZAUIK_itrkWP7irmih4im131Ume_wKzyks,9211 +babel/locale-data/yrl_VE.dat,sha256=_Tqu_pGUkzrd4M9SKXpn4g93z7Q5xlUMAtGvROS5x6M,9211 +babel/locale-data/yue.dat,sha256=lvTaoHXSVZHqcb5UCR8IeDiVRCE9IRsrDkhv0bGwzCg,143872 +babel/locale-data/yue_Hans.dat,sha256=ybTM8LU-H-QunhqnLmCQ9bE6zdsYMJPHdlh_hBv3AWk,145194 +babel/locale-data/yue_Hans_CN.dat,sha256=MOZnu5r0xokf672rwAXfgzG3VEeTfNlDbu36fR1bB6I,636 +babel/locale-data/yue_Hant.dat,sha256=76wefuhsGIjk0jT05KSTEU_pbb7AfNE2u56jdRoJaDM,1306 +babel/locale-data/yue_Hant_CN.dat,sha256=MOZnu5r0xokf672rwAXfgzG3VEeTfNlDbu36fR1bB6I,636 +babel/locale-data/yue_Hant_HK.dat,sha256=eMZ7mOTEyKzQz0MDIjvP_Z57CS9nFIP4-Uf0Jpx1CY4,636 +babel/locale-data/za.dat,sha256=5EbAmE-qqvt_e4geaFxBGJi81CVoZFK4dCcJ346P4yU,12705 +babel/locale-data/za_CN.dat,sha256=WzhnaughSM1C1twuKYBVrelLEer-guzT43a9yBNXCsg,635 +babel/locale-data/zgh.dat,sha256=Q2D9KQFgDfWk4fXOdEbPcLq3yX_zxjhlK_PVI61Ok4U,22018 +babel/locale-data/zgh_MA.dat,sha256=sV8aPBmHY23lKIzsRna-tPz3UiXoKCMQ1Ur2tCyx2YA,617 +babel/locale-data/zh.dat,sha256=SpSkmdVN5PX7CCdso1B-SyQZKMDTyZfZeT2l_kj2FoI,152100 +babel/locale-data/zh_Hans.dat,sha256=-wR64b65stjA2KWj4GWcBWiF3yI0A24p4K7wApj8qtc,1305 +babel/locale-data/zh_Hans_CN.dat,sha256=zz2qPpWEP9ba12T9cGsPXGI7Bg5LcmZ3NkAMiVTf61A,635 +babel/locale-data/zh_Hans_HK.dat,sha256=yPyZyU-xXSNyU1FYJIYpWwnh0d3uXOra9V9aj2j_YQU,3621 +babel/locale-data/zh_Hans_MO.dat,sha256=08kUXwHOXA0pRA8qIX7e4p0Nzl1XHSBjwhMh7f-nZBE,3752 +babel/locale-data/zh_Hans_MY.dat,sha256=iEFxl2XEm1zBzd1K0wXpiZFvU7l0UrNURh2p9DZdRLU,1300 +babel/locale-data/zh_Hans_SG.dat,sha256=OHZNiUfnteSXuzel-lZAMVgv58iiVTNhjq81T9Qu94s,3948 +babel/locale-data/zh_Hant.dat,sha256=31aRGt18bl6MdiI9iAozwltdkSQQ370TvzI8gid8CBI,154802 +babel/locale-data/zh_Hant_HK.dat,sha256=NXPCE_4pYgLatoeKLdQ0d8PQ-Mk_GeMF1s1JYX9m27M,49152 +babel/locale-data/zh_Hant_MO.dat,sha256=qCWPPEr87U1aemaIuf6nCYQ-Q3w_1HcXrYoncYVBd3g,657 +babel/locale-data/zh_Hant_MY.dat,sha256=3n4syQpQ9sXVe82sOEH7yQIdrU3dAtMhp7fEhzGN8_E,1203 +babel/locale-data/zh_Hant_TW.dat,sha256=5N4K8I3XG89knCaSoy97HGzTVOTiUcd_VmfvNJlA4Bg,635 +babel/locale-data/zh_Latn.dat,sha256=-wR64b65stjA2KWj4GWcBWiF3yI0A24p4K7wApj8qtc,1305 +babel/locale-data/zh_Latn_CN.dat,sha256=zz2qPpWEP9ba12T9cGsPXGI7Bg5LcmZ3NkAMiVTf61A,635 +babel/locale-data/zu.dat,sha256=e48g3oI1FjkROV3yHZ2GAwn_8wr2y3dMkj98NHW4X8Q,138626 +babel/locale-data/zu_ZA.dat,sha256=jHloBfkNbQETXgE-3xVxvESitWb994_-SoY0G4_JL5E,635 +babel/localedata.py,sha256=gzh3fMZIkA0k70eqXSRA46nbGWl-4BfQnB59czUmp7o,9116 +babel/localtime/__init__.py,sha256=_6ed9ZDImaEkVQdCbNitxCvRS7Cljpa5wsah0fl_628,1043 +babel/localtime/__pycache__/__init__.cpython-312.pyc,, +babel/localtime/__pycache__/_fallback.cpython-312.pyc,, +babel/localtime/__pycache__/_helpers.cpython-312.pyc,, +babel/localtime/__pycache__/_unix.cpython-312.pyc,, +babel/localtime/__pycache__/_win32.cpython-312.pyc,, +babel/localtime/_fallback.py,sha256=kBPMTXycRen_6lRxSj-VCGcXmzD1gklJpJ7ouxGLuMU,1207 +babel/localtime/_helpers.py,sha256=ZmLc8m46W-3GtsstLBdrw6BU3ZhSob8vKHkxgcZG9uw,1704 +babel/localtime/_unix.py,sha256=63rA_pJWM5FEKpJ3XAChL6Eosu5k-tla7C6n4mBkpyQ,3902 +babel/localtime/_win32.py,sha256=nSNnxSMOVAlvqvX50h9HSCzBfP7mj8njJ80JZ3pOIWk,3211 +babel/messages/__init__.py,sha256=1maeVkLsWdXYovEAxGOcGlNHM80PfphZmmivZJo5yTw,349 +babel/messages/__pycache__/__init__.cpython-312.pyc,, +babel/messages/__pycache__/_compat.cpython-312.pyc,, +babel/messages/__pycache__/catalog.cpython-312.pyc,, +babel/messages/__pycache__/checkers.cpython-312.pyc,, +babel/messages/__pycache__/extract.cpython-312.pyc,, +babel/messages/__pycache__/frontend.cpython-312.pyc,, +babel/messages/__pycache__/jslexer.cpython-312.pyc,, +babel/messages/__pycache__/mofile.cpython-312.pyc,, +babel/messages/__pycache__/plurals.cpython-312.pyc,, +babel/messages/__pycache__/pofile.cpython-312.pyc,, +babel/messages/__pycache__/setuptools_frontend.cpython-312.pyc,, +babel/messages/_compat.py,sha256=DNyCNMwH5vXgl_pq7cUYz3VcRC2gIPqeQLO5nN5neWE,1163 +babel/messages/catalog.py,sha256=2231tYkaywOeeb96nxm13aT9rLU9zPCqyA-hedf_QTc,37802 +babel/messages/checkers.py,sha256=VutlPZQQ_a4CqsGBNrywgC5QyW5yOhGpwZ2T3lNemqY,6219 +babel/messages/extract.py,sha256=NRMXMPjsdzirUdmgT3SFWkeZnRQUpj3VGcUPX7AxsPs,34274 +babel/messages/frontend.py,sha256=WsxpORPikJ8uhu7TM290y6d7Vxw35edYeGBlECLNkss,45408 +babel/messages/jslexer.py,sha256=6nGIZfX0dQlY0wlJ1zy5yWwoOaELY_ZWV26a5uKdsIQ,7207 +babel/messages/mofile.py,sha256=Qf0X-vIRYTQQpSJqWFY3nXPgCzxyUWAzrtle2fIpIPU,7265 +babel/messages/plurals.py,sha256=jsEnofU5b4PZKIloX0MjchYSxD3r1RRZ_GfEVRuETWQ,7495 +babel/messages/pofile.py,sha256=lMllxTXYRBa3m8wVsxXsJsayjCOePKYYZtFSZU5DVF4,25955 +babel/messages/setuptools_frontend.py,sha256=m1l9NHuawj1pSncZeC82cUJfdwxib_C7JSUb_2EhbUM,3485 +babel/numbers.py,sha256=GhjFEdksEkfmvPN7VjGjlptbtmVaboBZdRaB5zPMZvY,62758 +babel/plural.py,sha256=9NWPLtYleF8gsi23S9OIRth4WhND56E5fdFatwVb-uI,23112 +babel/py.typed,sha256=DtCsIDq6KOv2NOEdQjTbeMWJKRh6ZEL2E-6Mf1RLeMA,59 +babel/support.py,sha256=PLxaklxjez4pysEQ1n3rGbGMRKOPkOeszy73RZ1qctU,27516 +babel/units.py,sha256=K8CDi8z6u9envHjP1s2_TqDvVtbeg6zBN6QuksrtjIU,13753 +babel/util.py,sha256=gC2c7exKpi9yDALyoL4yGGW3S-BkADAZw9dIWXw95TM,9071 diff --git a/venv/lib/python3.12/site-packages/babel-2.17.0.dist-info/WHEEL b/venv/lib/python3.12/site-packages/babel-2.17.0.dist-info/WHEEL new file mode 100644 index 00000000..505164bc --- /dev/null +++ b/venv/lib/python3.12/site-packages/babel-2.17.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (75.8.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/venv/lib/python3.12/site-packages/babel-2.17.0.dist-info/entry_points.txt b/venv/lib/python3.12/site-packages/babel-2.17.0.dist-info/entry_points.txt new file mode 100644 index 00000000..95235a55 --- /dev/null +++ b/venv/lib/python3.12/site-packages/babel-2.17.0.dist-info/entry_points.txt @@ -0,0 +1,20 @@ +[babel.checkers] +num_plurals = babel.messages.checkers:num_plurals +python_format = babel.messages.checkers:python_format + +[babel.extractors] +ignore = babel.messages.extract:extract_nothing +javascript = babel.messages.extract:extract_javascript +python = babel.messages.extract:extract_python + +[console_scripts] +pybabel = babel.messages.frontend:main + +[distutils.commands] +compile_catalog = babel.messages.setuptools_frontend:compile_catalog +extract_messages = babel.messages.setuptools_frontend:extract_messages +init_catalog = babel.messages.setuptools_frontend:init_catalog +update_catalog = babel.messages.setuptools_frontend:update_catalog + +[distutils.setup_keywords] +message_extractors = babel.messages.setuptools_frontend:check_message_extractors diff --git a/venv/lib/python3.12/site-packages/babel-2.17.0.dist-info/top_level.txt b/venv/lib/python3.12/site-packages/babel-2.17.0.dist-info/top_level.txt new file mode 100644 index 00000000..98f65931 --- /dev/null +++ b/venv/lib/python3.12/site-packages/babel-2.17.0.dist-info/top_level.txt @@ -0,0 +1 @@ +babel diff --git a/venv/lib/python3.12/site-packages/babel/__init__.py b/venv/lib/python3.12/site-packages/babel/__init__.py new file mode 100644 index 00000000..7b277455 --- /dev/null +++ b/venv/lib/python3.12/site-packages/babel/__init__.py @@ -0,0 +1,38 @@ +""" + babel + ~~~~~ + + Integrated collection of utilities that assist in internationalizing and + localizing applications. + + This package is basically composed of two major parts: + + * tools to build and work with ``gettext`` message catalogs + * a Python interface to the CLDR (Common Locale Data Repository), providing + access to various locale display names, localized number and date + formatting, etc. + + :copyright: (c) 2013-2025 by the Babel Team. + :license: BSD, see LICENSE for more details. +""" + +from babel.core import ( + Locale, + UnknownLocaleError, + default_locale, + get_locale_identifier, + negotiate_locale, + parse_locale, +) + +__version__ = '2.17.0' + +__all__ = [ + 'Locale', + 'UnknownLocaleError', + '__version__', + 'default_locale', + 'get_locale_identifier', + 'negotiate_locale', + 'parse_locale', +] diff --git a/venv/lib/python3.12/site-packages/babel/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/babel/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 00000000..8eb5c503 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/babel/__pycache__/core.cpython-312.pyc b/venv/lib/python3.12/site-packages/babel/__pycache__/core.cpython-312.pyc new file mode 100644 index 00000000..439f4026 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/__pycache__/core.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/babel/__pycache__/dates.cpython-312.pyc b/venv/lib/python3.12/site-packages/babel/__pycache__/dates.cpython-312.pyc new file mode 100644 index 00000000..449a503a Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/__pycache__/dates.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/babel/__pycache__/languages.cpython-312.pyc b/venv/lib/python3.12/site-packages/babel/__pycache__/languages.cpython-312.pyc new file mode 100644 index 00000000..363be388 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/__pycache__/languages.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/babel/__pycache__/lists.cpython-312.pyc b/venv/lib/python3.12/site-packages/babel/__pycache__/lists.cpython-312.pyc new file mode 100644 index 00000000..f432490e Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/__pycache__/lists.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/babel/__pycache__/localedata.cpython-312.pyc b/venv/lib/python3.12/site-packages/babel/__pycache__/localedata.cpython-312.pyc new file mode 100644 index 00000000..c96ee440 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/__pycache__/localedata.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/babel/__pycache__/numbers.cpython-312.pyc b/venv/lib/python3.12/site-packages/babel/__pycache__/numbers.cpython-312.pyc new file mode 100644 index 00000000..0eab99f4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/__pycache__/numbers.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/babel/__pycache__/plural.cpython-312.pyc b/venv/lib/python3.12/site-packages/babel/__pycache__/plural.cpython-312.pyc new file mode 100644 index 00000000..b3b12859 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/__pycache__/plural.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/babel/__pycache__/support.cpython-312.pyc b/venv/lib/python3.12/site-packages/babel/__pycache__/support.cpython-312.pyc new file mode 100644 index 00000000..d7a0be88 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/__pycache__/support.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/babel/__pycache__/units.cpython-312.pyc b/venv/lib/python3.12/site-packages/babel/__pycache__/units.cpython-312.pyc new file mode 100644 index 00000000..b63dc5c8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/__pycache__/units.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/babel/__pycache__/util.cpython-312.pyc b/venv/lib/python3.12/site-packages/babel/__pycache__/util.cpython-312.pyc new file mode 100644 index 00000000..3bda1f50 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/__pycache__/util.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/babel/core.py b/venv/lib/python3.12/site-packages/babel/core.py new file mode 100644 index 00000000..5762bbe3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/babel/core.py @@ -0,0 +1,1337 @@ +""" + babel.core + ~~~~~~~~~~ + + Core locale representation and locale data access. + + :copyright: (c) 2013-2025 by the Babel Team. + :license: BSD, see LICENSE for more details. +""" + +from __future__ import annotations + +import os +import pickle +from collections.abc import Iterable, Mapping +from typing import TYPE_CHECKING, Any, Literal + +from babel import localedata +from babel.plural import PluralRule + +__all__ = [ + 'Locale', + 'UnknownLocaleError', + 'default_locale', + 'get_global', + 'get_locale_identifier', + 'negotiate_locale', + 'parse_locale', +] + +if TYPE_CHECKING: + from typing_extensions import TypeAlias + + _GLOBAL_KEY: TypeAlias = Literal[ + "all_currencies", + "currency_fractions", + "language_aliases", + "likely_subtags", + "meta_zones", + "parent_exceptions", + "script_aliases", + "territory_aliases", + "territory_currencies", + "territory_languages", + "territory_zones", + "variant_aliases", + "windows_zone_mapping", + "zone_aliases", + "zone_territories", + ] + + _global_data: Mapping[_GLOBAL_KEY, Mapping[str, Any]] | None + +_global_data = None +_default_plural_rule = PluralRule({}) + + +def _raise_no_data_error(): + raise RuntimeError('The babel data files are not available. ' + 'This usually happens because you are using ' + 'a source checkout from Babel and you did ' + 'not build the data files. Just make sure ' + 'to run "python setup.py import_cldr" before ' + 'installing the library.') + + +def get_global(key: _GLOBAL_KEY) -> Mapping[str, Any]: + """Return the dictionary for the given key in the global data. + + The global data is stored in the ``babel/global.dat`` file and contains + information independent of individual locales. + + >>> get_global('zone_aliases')['UTC'] + u'Etc/UTC' + >>> get_global('zone_territories')['Europe/Berlin'] + u'DE' + + The keys available are: + + - ``all_currencies`` + - ``currency_fractions`` + - ``language_aliases`` + - ``likely_subtags`` + - ``parent_exceptions`` + - ``script_aliases`` + - ``territory_aliases`` + - ``territory_currencies`` + - ``territory_languages`` + - ``territory_zones`` + - ``variant_aliases`` + - ``windows_zone_mapping`` + - ``zone_aliases`` + - ``zone_territories`` + + .. note:: The internal structure of the data may change between versions. + + .. versionadded:: 0.9 + + :param key: the data key + """ + global _global_data + if _global_data is None: + dirname = os.path.join(os.path.dirname(__file__)) + filename = os.path.join(dirname, 'global.dat') + if not os.path.isfile(filename): + _raise_no_data_error() + with open(filename, 'rb') as fileobj: + _global_data = pickle.load(fileobj) + assert _global_data is not None + return _global_data.get(key, {}) + + +LOCALE_ALIASES = { + 'ar': 'ar_SY', 'bg': 'bg_BG', 'bs': 'bs_BA', 'ca': 'ca_ES', 'cs': 'cs_CZ', + 'da': 'da_DK', 'de': 'de_DE', 'el': 'el_GR', 'en': 'en_US', 'es': 'es_ES', + 'et': 'et_EE', 'fa': 'fa_IR', 'fi': 'fi_FI', 'fr': 'fr_FR', 'gl': 'gl_ES', + 'he': 'he_IL', 'hu': 'hu_HU', 'id': 'id_ID', 'is': 'is_IS', 'it': 'it_IT', + 'ja': 'ja_JP', 'km': 'km_KH', 'ko': 'ko_KR', 'lt': 'lt_LT', 'lv': 'lv_LV', + 'mk': 'mk_MK', 'nl': 'nl_NL', 'nn': 'nn_NO', 'no': 'nb_NO', 'pl': 'pl_PL', + 'pt': 'pt_PT', 'ro': 'ro_RO', 'ru': 'ru_RU', 'sk': 'sk_SK', 'sl': 'sl_SI', + 'sv': 'sv_SE', 'th': 'th_TH', 'tr': 'tr_TR', 'uk': 'uk_UA', +} + + +class UnknownLocaleError(Exception): + """Exception thrown when a locale is requested for which no locale data + is available. + """ + + def __init__(self, identifier: str) -> None: + """Create the exception. + + :param identifier: the identifier string of the unsupported locale + """ + Exception.__init__(self, f"unknown locale {identifier!r}") + + #: The identifier of the locale that could not be found. + self.identifier = identifier + + +class Locale: + """Representation of a specific locale. + + >>> locale = Locale('en', 'US') + >>> repr(locale) + "Locale('en', territory='US')" + >>> locale.display_name + u'English (United States)' + + A `Locale` object can also be instantiated from a raw locale string: + + >>> locale = Locale.parse('en-US', sep='-') + >>> repr(locale) + "Locale('en', territory='US')" + + `Locale` objects provide access to a collection of locale data, such as + territory and language names, number and date format patterns, and more: + + >>> locale.number_symbols['latn']['decimal'] + u'.' + + If a locale is requested for which no locale data is available, an + `UnknownLocaleError` is raised: + + >>> Locale.parse('en_XX') + Traceback (most recent call last): + ... + UnknownLocaleError: unknown locale 'en_XX' + + For more information see :rfc:`3066`. + """ + + def __init__( + self, + language: str, + territory: str | None = None, + script: str | None = None, + variant: str | None = None, + modifier: str | None = None, + ) -> None: + """Initialize the locale object from the given identifier components. + + >>> locale = Locale('en', 'US') + >>> locale.language + 'en' + >>> locale.territory + 'US' + + :param language: the language code + :param territory: the territory (country or region) code + :param script: the script code + :param variant: the variant code + :param modifier: a modifier (following the '@' symbol, sometimes called '@variant') + :raise `UnknownLocaleError`: if no locale data is available for the + requested locale + """ + #: the language code + self.language = language + #: the territory (country or region) code + self.territory = territory + #: the script code + self.script = script + #: the variant code + self.variant = variant + #: the modifier + self.modifier = modifier + self.__data: localedata.LocaleDataDict | None = None + + identifier = str(self) + identifier_without_modifier = identifier.partition('@')[0] + if localedata.exists(identifier): + self.__data_identifier = identifier + elif localedata.exists(identifier_without_modifier): + self.__data_identifier = identifier_without_modifier + else: + raise UnknownLocaleError(identifier) + + @classmethod + def default(cls, category: str | None = None, aliases: Mapping[str, str] = LOCALE_ALIASES) -> Locale: + """Return the system default locale for the specified category. + + >>> for name in ['LANGUAGE', 'LC_ALL', 'LC_CTYPE', 'LC_MESSAGES']: + ... os.environ[name] = '' + >>> os.environ['LANG'] = 'fr_FR.UTF-8' + >>> Locale.default('LC_MESSAGES') + Locale('fr', territory='FR') + + The following fallbacks to the variable are always considered: + + - ``LANGUAGE`` + - ``LC_ALL`` + - ``LC_CTYPE`` + - ``LANG`` + + :param category: one of the ``LC_XXX`` environment variable names + :param aliases: a dictionary of aliases for locale identifiers + """ + # XXX: use likely subtag expansion here instead of the + # aliases dictionary. + locale_string = default_locale(category, aliases=aliases) + return cls.parse(locale_string) + + @classmethod + def negotiate( + cls, + preferred: Iterable[str], + available: Iterable[str], + sep: str = '_', + aliases: Mapping[str, str] = LOCALE_ALIASES, + ) -> Locale | None: + """Find the best match between available and requested locale strings. + + >>> Locale.negotiate(['de_DE', 'en_US'], ['de_DE', 'de_AT']) + Locale('de', territory='DE') + >>> Locale.negotiate(['de_DE', 'en_US'], ['en', 'de']) + Locale('de') + >>> Locale.negotiate(['de_DE', 'de'], ['en_US']) + + You can specify the character used in the locale identifiers to separate + the different components. This separator is applied to both lists. Also, + case is ignored in the comparison: + + >>> Locale.negotiate(['de-DE', 'de'], ['en-us', 'de-de'], sep='-') + Locale('de', territory='DE') + + :param preferred: the list of locale identifiers preferred by the user + :param available: the list of locale identifiers available + :param aliases: a dictionary of aliases for locale identifiers + :param sep: separator for parsing; e.g. Windows tends to use '-' instead of '_'. + """ + identifier = negotiate_locale(preferred, available, sep=sep, + aliases=aliases) + if identifier: + return Locale.parse(identifier, sep=sep) + return None + + @classmethod + def parse( + cls, + identifier: Locale | str | None, + sep: str = '_', + resolve_likely_subtags: bool = True, + ) -> Locale: + """Create a `Locale` instance for the given locale identifier. + + >>> l = Locale.parse('de-DE', sep='-') + >>> l.display_name + u'Deutsch (Deutschland)' + + If the `identifier` parameter is not a string, but actually a `Locale` + object, that object is returned: + + >>> Locale.parse(l) + Locale('de', territory='DE') + + If the `identifier` parameter is neither of these, such as `None` + or an empty string, e.g. because a default locale identifier + could not be determined, a `TypeError` is raised: + + >>> Locale.parse(None) + Traceback (most recent call last): + ... + TypeError: ... + + This also can perform resolving of likely subtags which it does + by default. This is for instance useful to figure out the most + likely locale for a territory you can use ``'und'`` as the + language tag: + + >>> Locale.parse('und_AT') + Locale('de', territory='AT') + + Modifiers are optional, and always at the end, separated by "@": + + >>> Locale.parse('de_AT@euro') + Locale('de', territory='AT', modifier='euro') + + :param identifier: the locale identifier string + :param sep: optional component separator + :param resolve_likely_subtags: if this is specified then a locale will + have its likely subtag resolved if the + locale otherwise does not exist. For + instance ``zh_TW`` by itself is not a + locale that exists but Babel can + automatically expand it to the full + form of ``zh_hant_TW``. Note that this + expansion is only taking place if no + locale exists otherwise. For instance + there is a locale ``en`` that can exist + by itself. + :raise `ValueError`: if the string does not appear to be a valid locale + identifier + :raise `UnknownLocaleError`: if no locale data is available for the + requested locale + :raise `TypeError`: if the identifier is not a string or a `Locale` + :raise `ValueError`: if the identifier is not a valid string + """ + if isinstance(identifier, Locale): + return identifier + + if not identifier: + msg = ( + f"Empty locale identifier value: {identifier!r}\n\n" + f"If you didn't explicitly pass an empty value to a Babel function, " + f"this could be caused by there being no suitable locale environment " + f"variables for the API you tried to use.", + ) + if isinstance(identifier, str): + raise ValueError(msg) # `parse_locale` would raise a ValueError, so let's do that here + raise TypeError(msg) + + if not isinstance(identifier, str): + raise TypeError(f"Unexpected value for identifier: {identifier!r}") + + parts = parse_locale(identifier, sep=sep) + input_id = get_locale_identifier(parts) + + def _try_load(parts): + try: + return cls(*parts) + except UnknownLocaleError: + return None + + def _try_load_reducing(parts): + # Success on first hit, return it. + locale = _try_load(parts) + if locale is not None: + return locale + + # Now try without script and variant + locale = _try_load(parts[:2]) + if locale is not None: + return locale + + locale = _try_load(parts) + if locale is not None: + return locale + if not resolve_likely_subtags: + raise UnknownLocaleError(input_id) + + # From here onwards is some very bad likely subtag resolving. This + # whole logic is not entirely correct but good enough (tm) for the + # time being. This has been added so that zh_TW does not cause + # errors for people when they upgrade. Later we should properly + # implement ICU like fuzzy locale objects and provide a way to + # maximize and minimize locale tags. + + if len(parts) == 5: + language, territory, script, variant, modifier = parts + else: + language, territory, script, variant = parts + modifier = None + language = get_global('language_aliases').get(language, language) + territory = get_global('territory_aliases').get(territory or '', (territory,))[0] + script = get_global('script_aliases').get(script or '', script) + variant = get_global('variant_aliases').get(variant or '', variant) + + if territory == 'ZZ': + territory = None + if script == 'Zzzz': + script = None + + parts = language, territory, script, variant, modifier + + # First match: try the whole identifier + new_id = get_locale_identifier(parts) + likely_subtag = get_global('likely_subtags').get(new_id) + if likely_subtag is not None: + locale = _try_load_reducing(parse_locale(likely_subtag)) + if locale is not None: + return locale + + # If we did not find anything so far, try again with a + # simplified identifier that is just the language + likely_subtag = get_global('likely_subtags').get(language) + if likely_subtag is not None: + parts2 = parse_locale(likely_subtag) + if len(parts2) == 5: + language2, _, script2, variant2, modifier2 = parts2 + else: + language2, _, script2, variant2 = parts2 + modifier2 = None + locale = _try_load_reducing((language2, territory, script2, variant2, modifier2)) + if locale is not None: + return locale + + raise UnknownLocaleError(input_id) + + def __eq__(self, other: object) -> bool: + for key in ('language', 'territory', 'script', 'variant', 'modifier'): + if not hasattr(other, key): + return False + return ( + self.language == getattr(other, 'language') and # noqa: B009 + self.territory == getattr(other, 'territory') and # noqa: B009 + self.script == getattr(other, 'script') and # noqa: B009 + self.variant == getattr(other, 'variant') and # noqa: B009 + self.modifier == getattr(other, 'modifier') # noqa: B009 + ) + + def __ne__(self, other: object) -> bool: + return not self.__eq__(other) + + def __hash__(self) -> int: + return hash((self.language, self.territory, self.script, + self.variant, self.modifier)) + + def __repr__(self) -> str: + parameters = [''] + for key in ('territory', 'script', 'variant', 'modifier'): + value = getattr(self, key) + if value is not None: + parameters.append(f"{key}={value!r}") + return f"Locale({self.language!r}{', '.join(parameters)})" + + def __str__(self) -> str: + return get_locale_identifier((self.language, self.territory, + self.script, self.variant, + self.modifier)) + + @property + def _data(self) -> localedata.LocaleDataDict: + if self.__data is None: + self.__data = localedata.LocaleDataDict(localedata.load(self.__data_identifier)) + return self.__data + + def get_display_name(self, locale: Locale | str | None = None) -> str | None: + """Return the display name of the locale using the given locale. + + The display name will include the language, territory, script, and + variant, if those are specified. + + >>> Locale('zh', 'CN', script='Hans').get_display_name('en') + u'Chinese (Simplified, China)' + + Modifiers are currently passed through verbatim: + + >>> Locale('it', 'IT', modifier='euro').get_display_name('en') + u'Italian (Italy, euro)' + + :param locale: the locale to use + """ + if locale is None: + locale = self + locale = Locale.parse(locale) + retval = locale.languages.get(self.language) + if retval and (self.territory or self.script or self.variant): + details = [] + if self.script: + details.append(locale.scripts.get(self.script)) + if self.territory: + details.append(locale.territories.get(self.territory)) + if self.variant: + details.append(locale.variants.get(self.variant)) + if self.modifier: + details.append(self.modifier) + detail_string = ', '.join(atom for atom in details if atom) + if detail_string: + retval += f" ({detail_string})" + return retval + + display_name = property(get_display_name, doc="""\ + The localized display name of the locale. + + >>> Locale('en').display_name + u'English' + >>> Locale('en', 'US').display_name + u'English (United States)' + >>> Locale('sv').display_name + u'svenska' + + :type: `unicode` + """) + + def get_language_name(self, locale: Locale | str | None = None) -> str | None: + """Return the language of this locale in the given locale. + + >>> Locale('zh', 'CN', script='Hans').get_language_name('de') + u'Chinesisch' + + .. versionadded:: 1.0 + + :param locale: the locale to use + """ + if locale is None: + locale = self + locale = Locale.parse(locale) + return locale.languages.get(self.language) + + language_name = property(get_language_name, doc="""\ + The localized language name of the locale. + + >>> Locale('en', 'US').language_name + u'English' + """) + + def get_territory_name(self, locale: Locale | str | None = None) -> str | None: + """Return the territory name in the given locale.""" + if locale is None: + locale = self + locale = Locale.parse(locale) + return locale.territories.get(self.territory or '') + + territory_name = property(get_territory_name, doc="""\ + The localized territory name of the locale if available. + + >>> Locale('de', 'DE').territory_name + u'Deutschland' + """) + + def get_script_name(self, locale: Locale | str | None = None) -> str | None: + """Return the script name in the given locale.""" + if locale is None: + locale = self + locale = Locale.parse(locale) + return locale.scripts.get(self.script or '') + + script_name = property(get_script_name, doc="""\ + The localized script name of the locale if available. + + >>> Locale('sr', 'ME', script='Latn').script_name + u'latinica' + """) + + @property + def english_name(self) -> str | None: + """The english display name of the locale. + + >>> Locale('de').english_name + u'German' + >>> Locale('de', 'DE').english_name + u'German (Germany)' + + :type: `unicode`""" + return self.get_display_name(Locale('en')) + + # { General Locale Display Names + + @property + def languages(self) -> localedata.LocaleDataDict: + """Mapping of language codes to translated language names. + + >>> Locale('de', 'DE').languages['ja'] + u'Japanisch' + + See `ISO 639 `_ for + more information. + """ + return self._data['languages'] + + @property + def scripts(self) -> localedata.LocaleDataDict: + """Mapping of script codes to translated script names. + + >>> Locale('en', 'US').scripts['Hira'] + u'Hiragana' + + See `ISO 15924 `_ + for more information. + """ + return self._data['scripts'] + + @property + def territories(self) -> localedata.LocaleDataDict: + """Mapping of script codes to translated script names. + + >>> Locale('es', 'CO').territories['DE'] + u'Alemania' + + See `ISO 3166 `_ + for more information. + """ + return self._data['territories'] + + @property + def variants(self) -> localedata.LocaleDataDict: + """Mapping of script codes to translated script names. + + >>> Locale('de', 'DE').variants['1901'] + u'Alte deutsche Rechtschreibung' + """ + return self._data['variants'] + + # { Number Formatting + + @property + def currencies(self) -> localedata.LocaleDataDict: + """Mapping of currency codes to translated currency names. This + only returns the generic form of the currency name, not the count + specific one. If an actual number is requested use the + :func:`babel.numbers.get_currency_name` function. + + >>> Locale('en').currencies['COP'] + u'Colombian Peso' + >>> Locale('de', 'DE').currencies['COP'] + u'Kolumbianischer Peso' + """ + return self._data['currency_names'] + + @property + def currency_symbols(self) -> localedata.LocaleDataDict: + """Mapping of currency codes to symbols. + + >>> Locale('en', 'US').currency_symbols['USD'] + u'$' + >>> Locale('es', 'CO').currency_symbols['USD'] + u'US$' + """ + return self._data['currency_symbols'] + + @property + def number_symbols(self) -> localedata.LocaleDataDict: + """Symbols used in number formatting by number system. + + .. note:: The format of the value returned may change between + Babel versions. + + >>> Locale('fr', 'FR').number_symbols["latn"]['decimal'] + u',' + >>> Locale('fa', 'IR').number_symbols["arabext"]['decimal'] + u'٫' + >>> Locale('fa', 'IR').number_symbols["latn"]['decimal'] + u'.' + """ + return self._data['number_symbols'] + + @property + def other_numbering_systems(self) -> localedata.LocaleDataDict: + """ + Mapping of other numbering systems available for the locale. + See: https://www.unicode.org/reports/tr35/tr35-numbers.html#otherNumberingSystems + + >>> Locale('el', 'GR').other_numbering_systems['traditional'] + u'grek' + + .. note:: The format of the value returned may change between + Babel versions. + """ + return self._data['numbering_systems'] + + @property + def default_numbering_system(self) -> str: + """The default numbering system used by the locale. + >>> Locale('el', 'GR').default_numbering_system + u'latn' + """ + return self._data['default_numbering_system'] + + @property + def decimal_formats(self) -> localedata.LocaleDataDict: + """Locale patterns for decimal number formatting. + + .. note:: The format of the value returned may change between + Babel versions. + + >>> Locale('en', 'US').decimal_formats[None] + + """ + return self._data['decimal_formats'] + + @property + def compact_decimal_formats(self) -> localedata.LocaleDataDict: + """Locale patterns for compact decimal number formatting. + + .. note:: The format of the value returned may change between + Babel versions. + + >>> Locale('en', 'US').compact_decimal_formats["short"]["one"]["1000"] + + """ + return self._data['compact_decimal_formats'] + + @property + def currency_formats(self) -> localedata.LocaleDataDict: + """Locale patterns for currency number formatting. + + .. note:: The format of the value returned may change between + Babel versions. + + >>> Locale('en', 'US').currency_formats['standard'] + + >>> Locale('en', 'US').currency_formats['accounting'] + + """ + return self._data['currency_formats'] + + @property + def compact_currency_formats(self) -> localedata.LocaleDataDict: + """Locale patterns for compact currency number formatting. + + .. note:: The format of the value returned may change between + Babel versions. + + >>> Locale('en', 'US').compact_currency_formats["short"]["one"]["1000"] + + """ + return self._data['compact_currency_formats'] + + @property + def percent_formats(self) -> localedata.LocaleDataDict: + """Locale patterns for percent number formatting. + + .. note:: The format of the value returned may change between + Babel versions. + + >>> Locale('en', 'US').percent_formats[None] + + """ + return self._data['percent_formats'] + + @property + def scientific_formats(self) -> localedata.LocaleDataDict: + """Locale patterns for scientific number formatting. + + .. note:: The format of the value returned may change between + Babel versions. + + >>> Locale('en', 'US').scientific_formats[None] + + """ + return self._data['scientific_formats'] + + # { Calendar Information and Date Formatting + + @property + def periods(self) -> localedata.LocaleDataDict: + """Locale display names for day periods (AM/PM). + + >>> Locale('en', 'US').periods['am'] + u'AM' + """ + try: + return self._data['day_periods']['stand-alone']['wide'] + except KeyError: + return localedata.LocaleDataDict({}) # pragma: no cover + + @property + def day_periods(self) -> localedata.LocaleDataDict: + """Locale display names for various day periods (not necessarily only AM/PM). + + These are not meant to be used without the relevant `day_period_rules`. + """ + return self._data['day_periods'] + + @property + def day_period_rules(self) -> localedata.LocaleDataDict: + """Day period rules for the locale. Used by `get_period_id`. + """ + return self._data.get('day_period_rules', localedata.LocaleDataDict({})) + + @property + def days(self) -> localedata.LocaleDataDict: + """Locale display names for weekdays. + + >>> Locale('de', 'DE').days['format']['wide'][3] + u'Donnerstag' + """ + return self._data['days'] + + @property + def months(self) -> localedata.LocaleDataDict: + """Locale display names for months. + + >>> Locale('de', 'DE').months['format']['wide'][10] + u'Oktober' + """ + return self._data['months'] + + @property + def quarters(self) -> localedata.LocaleDataDict: + """Locale display names for quarters. + + >>> Locale('de', 'DE').quarters['format']['wide'][1] + u'1. Quartal' + """ + return self._data['quarters'] + + @property + def eras(self) -> localedata.LocaleDataDict: + """Locale display names for eras. + + .. note:: The format of the value returned may change between + Babel versions. + + >>> Locale('en', 'US').eras['wide'][1] + u'Anno Domini' + >>> Locale('en', 'US').eras['abbreviated'][0] + u'BC' + """ + return self._data['eras'] + + @property + def time_zones(self) -> localedata.LocaleDataDict: + """Locale display names for time zones. + + .. note:: The format of the value returned may change between + Babel versions. + + >>> Locale('en', 'US').time_zones['Europe/London']['long']['daylight'] + u'British Summer Time' + >>> Locale('en', 'US').time_zones['America/St_Johns']['city'] + u'St. John\u2019s' + """ + return self._data['time_zones'] + + @property + def meta_zones(self) -> localedata.LocaleDataDict: + """Locale display names for meta time zones. + + Meta time zones are basically groups of different Olson time zones that + have the same GMT offset and daylight savings time. + + .. note:: The format of the value returned may change between + Babel versions. + + >>> Locale('en', 'US').meta_zones['Europe_Central']['long']['daylight'] + u'Central European Summer Time' + + .. versionadded:: 0.9 + """ + return self._data['meta_zones'] + + @property + def zone_formats(self) -> localedata.LocaleDataDict: + """Patterns related to the formatting of time zones. + + .. note:: The format of the value returned may change between + Babel versions. + + >>> Locale('en', 'US').zone_formats['fallback'] + u'%(1)s (%(0)s)' + >>> Locale('pt', 'BR').zone_formats['region'] + u'Hor\\xe1rio %s' + + .. versionadded:: 0.9 + """ + return self._data['zone_formats'] + + @property + def first_week_day(self) -> int: + """The first day of a week, with 0 being Monday. + + >>> Locale('de', 'DE').first_week_day + 0 + >>> Locale('en', 'US').first_week_day + 6 + """ + return self._data['week_data']['first_day'] + + @property + def weekend_start(self) -> int: + """The day the weekend starts, with 0 being Monday. + + >>> Locale('de', 'DE').weekend_start + 5 + """ + return self._data['week_data']['weekend_start'] + + @property + def weekend_end(self) -> int: + """The day the weekend ends, with 0 being Monday. + + >>> Locale('de', 'DE').weekend_end + 6 + """ + return self._data['week_data']['weekend_end'] + + @property + def min_week_days(self) -> int: + """The minimum number of days in a week so that the week is counted as + the first week of a year or month. + + >>> Locale('de', 'DE').min_week_days + 4 + """ + return self._data['week_data']['min_days'] + + @property + def date_formats(self) -> localedata.LocaleDataDict: + """Locale patterns for date formatting. + + .. note:: The format of the value returned may change between + Babel versions. + + >>> Locale('en', 'US').date_formats['short'] + + >>> Locale('fr', 'FR').date_formats['long'] + + """ + return self._data['date_formats'] + + @property + def time_formats(self) -> localedata.LocaleDataDict: + """Locale patterns for time formatting. + + .. note:: The format of the value returned may change between + Babel versions. + + >>> Locale('en', 'US').time_formats['short'] + + >>> Locale('fr', 'FR').time_formats['long'] + + """ + return self._data['time_formats'] + + @property + def datetime_formats(self) -> localedata.LocaleDataDict: + """Locale patterns for datetime formatting. + + .. note:: The format of the value returned may change between + Babel versions. + + >>> Locale('en').datetime_formats['full'] + u'{1}, {0}' + >>> Locale('th').datetime_formats['medium'] + u'{1} {0}' + """ + return self._data['datetime_formats'] + + @property + def datetime_skeletons(self) -> localedata.LocaleDataDict: + """Locale patterns for formatting parts of a datetime. + + >>> Locale('en').datetime_skeletons['MEd'] + + >>> Locale('fr').datetime_skeletons['MEd'] + + >>> Locale('fr').datetime_skeletons['H'] + + """ + return self._data['datetime_skeletons'] + + @property + def interval_formats(self) -> localedata.LocaleDataDict: + """Locale patterns for interval formatting. + + .. note:: The format of the value returned may change between + Babel versions. + + How to format date intervals in Finnish when the day is the + smallest changing component: + + >>> Locale('fi_FI').interval_formats['MEd']['d'] + [u'E d.\u2009\u2013\u2009', u'E d.M.'] + + .. seealso:: + + The primary API to use this data is :py:func:`babel.dates.format_interval`. + + + :rtype: dict[str, dict[str, list[str]]] + """ + return self._data['interval_formats'] + + @property + def plural_form(self) -> PluralRule: + """Plural rules for the locale. + + >>> Locale('en').plural_form(1) + 'one' + >>> Locale('en').plural_form(0) + 'other' + >>> Locale('fr').plural_form(0) + 'one' + >>> Locale('ru').plural_form(100) + 'many' + """ + return self._data.get('plural_form', _default_plural_rule) + + @property + def list_patterns(self) -> localedata.LocaleDataDict: + """Patterns for generating lists + + .. note:: The format of the value returned may change between + Babel versions. + + >>> Locale('en').list_patterns['standard']['start'] + u'{0}, {1}' + >>> Locale('en').list_patterns['standard']['end'] + u'{0}, and {1}' + >>> Locale('en_GB').list_patterns['standard']['end'] + u'{0} and {1}' + """ + return self._data['list_patterns'] + + @property + def ordinal_form(self) -> PluralRule: + """Plural rules for the locale. + + >>> Locale('en').ordinal_form(1) + 'one' + >>> Locale('en').ordinal_form(2) + 'two' + >>> Locale('en').ordinal_form(3) + 'few' + >>> Locale('fr').ordinal_form(2) + 'other' + >>> Locale('ru').ordinal_form(100) + 'other' + """ + return self._data.get('ordinal_form', _default_plural_rule) + + @property + def measurement_systems(self) -> localedata.LocaleDataDict: + """Localized names for various measurement systems. + + >>> Locale('fr', 'FR').measurement_systems['US'] + u'am\\xe9ricain' + >>> Locale('en', 'US').measurement_systems['US'] + u'US' + + """ + return self._data['measurement_systems'] + + @property + def character_order(self) -> str: + """The text direction for the language. + + >>> Locale('de', 'DE').character_order + 'left-to-right' + >>> Locale('ar', 'SA').character_order + 'right-to-left' + """ + return self._data['character_order'] + + @property + def text_direction(self) -> str: + """The text direction for the language in CSS short-hand form. + + >>> Locale('de', 'DE').text_direction + 'ltr' + >>> Locale('ar', 'SA').text_direction + 'rtl' + """ + return ''.join(word[0] for word in self.character_order.split('-')) + + @property + def unit_display_names(self) -> localedata.LocaleDataDict: + """Display names for units of measurement. + + .. seealso:: + + You may want to use :py:func:`babel.units.get_unit_name` instead. + + .. note:: The format of the value returned may change between + Babel versions. + + """ + return self._data['unit_display_names'] + + +def default_locale( + category: str | tuple[str, ...] | list[str] | None = None, + aliases: Mapping[str, str] = LOCALE_ALIASES, +) -> str | None: + """Returns the system default locale for a given category, based on + environment variables. + + >>> for name in ['LANGUAGE', 'LC_ALL', 'LC_CTYPE']: + ... os.environ[name] = '' + >>> os.environ['LANG'] = 'fr_FR.UTF-8' + >>> default_locale('LC_MESSAGES') + 'fr_FR' + + The "C" or "POSIX" pseudo-locales are treated as aliases for the + "en_US_POSIX" locale: + + >>> os.environ['LC_MESSAGES'] = 'POSIX' + >>> default_locale('LC_MESSAGES') + 'en_US_POSIX' + + The following fallbacks to the variable are always considered: + + - ``LANGUAGE`` + - ``LC_ALL`` + - ``LC_CTYPE`` + - ``LANG`` + + :param category: one or more of the ``LC_XXX`` environment variable names + :param aliases: a dictionary of aliases for locale identifiers + """ + + varnames = ('LANGUAGE', 'LC_ALL', 'LC_CTYPE', 'LANG') + if category: + if isinstance(category, str): + varnames = (category, *varnames) + elif isinstance(category, (list, tuple)): + varnames = (*category, *varnames) + else: + raise TypeError(f"Invalid type for category: {category!r}") + + for name in varnames: + if not name: + continue + locale = os.getenv(name) + if locale: + if name == 'LANGUAGE' and ':' in locale: + # the LANGUAGE variable may contain a colon-separated list of + # language codes; we just pick the language on the list + locale = locale.split(':')[0] + if locale.split('.')[0] in ('C', 'POSIX'): + locale = 'en_US_POSIX' + elif aliases and locale in aliases: + locale = aliases[locale] + try: + return get_locale_identifier(parse_locale(locale)) + except ValueError: + pass + return None + + +def negotiate_locale(preferred: Iterable[str], available: Iterable[str], sep: str = '_', aliases: Mapping[str, str] = LOCALE_ALIASES) -> str | None: + """Find the best match between available and requested locale strings. + + >>> negotiate_locale(['de_DE', 'en_US'], ['de_DE', 'de_AT']) + 'de_DE' + >>> negotiate_locale(['de_DE', 'en_US'], ['en', 'de']) + 'de' + + Case is ignored by the algorithm, the result uses the case of the preferred + locale identifier: + + >>> negotiate_locale(['de_DE', 'en_US'], ['de_de', 'de_at']) + 'de_DE' + + >>> negotiate_locale(['de_DE', 'en_US'], ['de_de', 'de_at']) + 'de_DE' + + By default, some web browsers unfortunately do not include the territory + in the locale identifier for many locales, and some don't even allow the + user to easily add the territory. So while you may prefer using qualified + locale identifiers in your web-application, they would not normally match + the language-only locale sent by such browsers. To workaround that, this + function uses a default mapping of commonly used language-only locale + identifiers to identifiers including the territory: + + >>> negotiate_locale(['ja', 'en_US'], ['ja_JP', 'en_US']) + 'ja_JP' + + Some browsers even use an incorrect or outdated language code, such as "no" + for Norwegian, where the correct locale identifier would actually be "nb_NO" + (Bokmål) or "nn_NO" (Nynorsk). The aliases are intended to take care of + such cases, too: + + >>> negotiate_locale(['no', 'sv'], ['nb_NO', 'sv_SE']) + 'nb_NO' + + You can override this default mapping by passing a different `aliases` + dictionary to this function, or you can bypass the behavior althogher by + setting the `aliases` parameter to `None`. + + :param preferred: the list of locale strings preferred by the user + :param available: the list of locale strings available + :param sep: character that separates the different parts of the locale + strings + :param aliases: a dictionary of aliases for locale identifiers + """ + available = [a.lower() for a in available if a] + for locale in preferred: + ll = locale.lower() + if ll in available: + return locale + if aliases: + alias = aliases.get(ll) + if alias: + alias = alias.replace('_', sep) + if alias.lower() in available: + return alias + parts = locale.split(sep) + if len(parts) > 1 and parts[0].lower() in available: + return parts[0] + return None + + +def parse_locale( + identifier: str, + sep: str = '_', +) -> tuple[str, str | None, str | None, str | None] | tuple[str, str | None, str | None, str | None, str | None]: + """Parse a locale identifier into a tuple of the form ``(language, + territory, script, variant, modifier)``. + + >>> parse_locale('zh_CN') + ('zh', 'CN', None, None) + >>> parse_locale('zh_Hans_CN') + ('zh', 'CN', 'Hans', None) + >>> parse_locale('ca_es_valencia') + ('ca', 'ES', None, 'VALENCIA') + >>> parse_locale('en_150') + ('en', '150', None, None) + >>> parse_locale('en_us_posix') + ('en', 'US', None, 'POSIX') + >>> parse_locale('it_IT@euro') + ('it', 'IT', None, None, 'euro') + >>> parse_locale('it_IT@custom') + ('it', 'IT', None, None, 'custom') + >>> parse_locale('it_IT@') + ('it', 'IT', None, None) + + The default component separator is "_", but a different separator can be + specified using the `sep` parameter. + + The optional modifier is always separated with "@" and at the end: + + >>> parse_locale('zh-CN', sep='-') + ('zh', 'CN', None, None) + >>> parse_locale('zh-CN@custom', sep='-') + ('zh', 'CN', None, None, 'custom') + + If the identifier cannot be parsed into a locale, a `ValueError` exception + is raised: + + >>> parse_locale('not_a_LOCALE_String') + Traceback (most recent call last): + ... + ValueError: 'not_a_LOCALE_String' is not a valid locale identifier + + Encoding information is removed from the identifier, while modifiers are + kept: + + >>> parse_locale('en_US.UTF-8') + ('en', 'US', None, None) + >>> parse_locale('de_DE.iso885915@euro') + ('de', 'DE', None, None, 'euro') + + See :rfc:`4646` for more information. + + :param identifier: the locale identifier string + :param sep: character that separates the different components of the locale + identifier + :raise `ValueError`: if the string does not appear to be a valid locale + identifier + """ + if not identifier: + raise ValueError("empty locale identifier") + identifier, _, modifier = identifier.partition('@') + if '.' in identifier: + # this is probably the charset/encoding, which we don't care about + identifier = identifier.split('.', 1)[0] + + parts = identifier.split(sep) + lang = parts.pop(0).lower() + if not lang.isalpha(): + raise ValueError(f"expected only letters, got {lang!r}") + + script = territory = variant = None + if parts and len(parts[0]) == 4 and parts[0].isalpha(): + script = parts.pop(0).title() + + if parts: + if len(parts[0]) == 2 and parts[0].isalpha(): + territory = parts.pop(0).upper() + elif len(parts[0]) == 3 and parts[0].isdigit(): + territory = parts.pop(0) + + if parts and ( + len(parts[0]) == 4 and parts[0][0].isdigit() or + len(parts[0]) >= 5 and parts[0][0].isalpha() + ): + variant = parts.pop().upper() + + if parts: + raise ValueError(f"{identifier!r} is not a valid locale identifier") + + # TODO(3.0): always return a 5-tuple + if modifier: + return lang, territory, script, variant, modifier + else: + return lang, territory, script, variant + + +def get_locale_identifier( + tup: tuple[str] + | tuple[str, str | None] + | tuple[str, str | None, str | None] + | tuple[str, str | None, str | None, str | None] + | tuple[str, str | None, str | None, str | None, str | None], + sep: str = "_", +) -> str: + """The reverse of :func:`parse_locale`. It creates a locale identifier out + of a ``(language, territory, script, variant, modifier)`` tuple. Items can be set to + ``None`` and trailing ``None``\\s can also be left out of the tuple. + + >>> get_locale_identifier(('de', 'DE', None, '1999', 'custom')) + 'de_DE_1999@custom' + >>> get_locale_identifier(('fi', None, None, None, 'custom')) + 'fi@custom' + + + .. versionadded:: 1.0 + + :param tup: the tuple as returned by :func:`parse_locale`. + :param sep: the separator for the identifier. + """ + tup = tuple(tup[:5]) # type: ignore # length should be no more than 5 + lang, territory, script, variant, modifier = tup + (None,) * (5 - len(tup)) + ret = sep.join(filter(None, (lang, script, territory, variant))) + return f'{ret}@{modifier}' if modifier else ret diff --git a/venv/lib/python3.12/site-packages/babel/dates.py b/venv/lib/python3.12/site-packages/babel/dates.py new file mode 100644 index 00000000..355a9236 --- /dev/null +++ b/venv/lib/python3.12/site-packages/babel/dates.py @@ -0,0 +1,1999 @@ +""" + babel.dates + ~~~~~~~~~~~ + + Locale dependent formatting and parsing of dates and times. + + The default locale for the functions in this module is determined by the + following environment variables, in that order: + + * ``LC_TIME``, + * ``LC_ALL``, and + * ``LANG`` + + :copyright: (c) 2013-2025 by the Babel Team. + :license: BSD, see LICENSE for more details. +""" + +from __future__ import annotations + +import math +import re +import warnings +from functools import lru_cache +from typing import TYPE_CHECKING, Literal, SupportsInt + +try: + import pytz +except ModuleNotFoundError: + pytz = None + import zoneinfo + +import datetime +from collections.abc import Iterable + +from babel import localtime +from babel.core import Locale, default_locale, get_global +from babel.localedata import LocaleDataDict + +if TYPE_CHECKING: + from typing_extensions import TypeAlias + _Instant: TypeAlias = datetime.date | datetime.time | float | None + _PredefinedTimeFormat: TypeAlias = Literal['full', 'long', 'medium', 'short'] + _Context: TypeAlias = Literal['format', 'stand-alone'] + _DtOrTzinfo: TypeAlias = datetime.datetime | datetime.tzinfo | str | int | datetime.time | None + +# "If a given short metazone form is known NOT to be understood in a given +# locale and the parent locale has this value such that it would normally +# be inherited, the inheritance of this value can be explicitly disabled by +# use of the 'no inheritance marker' as the value, which is 3 simultaneous [sic] +# empty set characters ( U+2205 )." +# - https://www.unicode.org/reports/tr35/tr35-dates.html#Metazone_Names + +NO_INHERITANCE_MARKER = '\u2205\u2205\u2205' + +UTC = datetime.timezone.utc +LOCALTZ = localtime.LOCALTZ + +LC_TIME = default_locale('LC_TIME') + + +def _localize(tz: datetime.tzinfo, dt: datetime.datetime) -> datetime.datetime: + # Support localizing with both pytz and zoneinfo tzinfos + # nothing to do + if dt.tzinfo is tz: + return dt + + if hasattr(tz, 'localize'): # pytz + return tz.localize(dt) + + if dt.tzinfo is None: + # convert naive to localized + return dt.replace(tzinfo=tz) + + # convert timezones + return dt.astimezone(tz) + + +def _get_dt_and_tzinfo(dt_or_tzinfo: _DtOrTzinfo) -> tuple[datetime.datetime | None, datetime.tzinfo]: + """ + Parse a `dt_or_tzinfo` value into a datetime and a tzinfo. + + See the docs for this function's callers for semantics. + + :rtype: tuple[datetime, tzinfo] + """ + if dt_or_tzinfo is None: + dt = datetime.datetime.now() + tzinfo = LOCALTZ + elif isinstance(dt_or_tzinfo, str): + dt = None + tzinfo = get_timezone(dt_or_tzinfo) + elif isinstance(dt_or_tzinfo, int): + dt = None + tzinfo = UTC + elif isinstance(dt_or_tzinfo, (datetime.datetime, datetime.time)): + dt = _get_datetime(dt_or_tzinfo) + tzinfo = dt.tzinfo if dt.tzinfo is not None else UTC + else: + dt = None + tzinfo = dt_or_tzinfo + return dt, tzinfo + + +def _get_tz_name(dt_or_tzinfo: _DtOrTzinfo) -> str: + """ + Get the timezone name out of a time, datetime, or tzinfo object. + + :rtype: str + """ + dt, tzinfo = _get_dt_and_tzinfo(dt_or_tzinfo) + if hasattr(tzinfo, 'zone'): # pytz object + return tzinfo.zone + elif hasattr(tzinfo, 'key') and tzinfo.key is not None: # ZoneInfo object + return tzinfo.key + else: + return tzinfo.tzname(dt or datetime.datetime.now(UTC)) + + +def _get_datetime(instant: _Instant) -> datetime.datetime: + """ + Get a datetime out of an "instant" (date, time, datetime, number). + + .. warning:: The return values of this function may depend on the system clock. + + If the instant is None, the current moment is used. + If the instant is a time, it's augmented with today's date. + + Dates are converted to naive datetimes with midnight as the time component. + + >>> from datetime import date, datetime + >>> _get_datetime(date(2015, 1, 1)) + datetime.datetime(2015, 1, 1, 0, 0) + + UNIX timestamps are converted to datetimes. + + >>> _get_datetime(1400000000) + datetime.datetime(2014, 5, 13, 16, 53, 20) + + Other values are passed through as-is. + + >>> x = datetime(2015, 1, 1) + >>> _get_datetime(x) is x + True + + :param instant: date, time, datetime, integer, float or None + :type instant: date|time|datetime|int|float|None + :return: a datetime + :rtype: datetime + """ + if instant is None: + return datetime.datetime.now(UTC).replace(tzinfo=None) + elif isinstance(instant, (int, float)): + return datetime.datetime.fromtimestamp(instant, UTC).replace(tzinfo=None) + elif isinstance(instant, datetime.time): + return datetime.datetime.combine(datetime.date.today(), instant) + elif isinstance(instant, datetime.date) and not isinstance(instant, datetime.datetime): + return datetime.datetime.combine(instant, datetime.time()) + # TODO (3.x): Add an assertion/type check for this fallthrough branch: + return instant + + +def _ensure_datetime_tzinfo(dt: datetime.datetime, tzinfo: datetime.tzinfo | None = None) -> datetime.datetime: + """ + Ensure the datetime passed has an attached tzinfo. + + If the datetime is tz-naive to begin with, UTC is attached. + + If a tzinfo is passed in, the datetime is normalized to that timezone. + + >>> from datetime import datetime + >>> _get_tz_name(_ensure_datetime_tzinfo(datetime(2015, 1, 1))) + 'UTC' + + >>> tz = get_timezone("Europe/Stockholm") + >>> _ensure_datetime_tzinfo(datetime(2015, 1, 1, 13, 15, tzinfo=UTC), tzinfo=tz).hour + 14 + + :param datetime: Datetime to augment. + :param tzinfo: optional tzinfo + :return: datetime with tzinfo + :rtype: datetime + """ + if dt.tzinfo is None: + dt = dt.replace(tzinfo=UTC) + if tzinfo is not None: + dt = dt.astimezone(get_timezone(tzinfo)) + if hasattr(tzinfo, 'normalize'): # pytz + dt = tzinfo.normalize(dt) + return dt + + +def _get_time( + time: datetime.time | datetime.datetime | None, + tzinfo: datetime.tzinfo | None = None, +) -> datetime.time: + """ + Get a timezoned time from a given instant. + + .. warning:: The return values of this function may depend on the system clock. + + :param time: time, datetime or None + :rtype: time + """ + if time is None: + time = datetime.datetime.now(UTC) + elif isinstance(time, (int, float)): + time = datetime.datetime.fromtimestamp(time, UTC) + + if time.tzinfo is None: + time = time.replace(tzinfo=UTC) + + if isinstance(time, datetime.datetime): + if tzinfo is not None: + time = time.astimezone(tzinfo) + if hasattr(tzinfo, 'normalize'): # pytz + time = tzinfo.normalize(time) + time = time.timetz() + elif tzinfo is not None: + time = time.replace(tzinfo=tzinfo) + return time + + +def get_timezone(zone: str | datetime.tzinfo | None = None) -> datetime.tzinfo: + """Looks up a timezone by name and returns it. The timezone object + returned comes from ``pytz`` or ``zoneinfo``, whichever is available. + It corresponds to the `tzinfo` interface and can be used with all of + the functions of Babel that operate with dates. + + If a timezone is not known a :exc:`LookupError` is raised. If `zone` + is ``None`` a local zone object is returned. + + :param zone: the name of the timezone to look up. If a timezone object + itself is passed in, it's returned unchanged. + """ + if zone is None: + return LOCALTZ + if not isinstance(zone, str): + return zone + + if pytz: + try: + return pytz.timezone(zone) + except pytz.UnknownTimeZoneError as e: + exc = e + else: + assert zoneinfo + try: + return zoneinfo.ZoneInfo(zone) + except zoneinfo.ZoneInfoNotFoundError as e: + exc = e + + raise LookupError(f"Unknown timezone {zone}") from exc + + +def get_period_names( + width: Literal['abbreviated', 'narrow', 'wide'] = 'wide', + context: _Context = 'stand-alone', + locale: Locale | str | None = None, +) -> LocaleDataDict: + """Return the names for day periods (AM/PM) used by the locale. + + >>> get_period_names(locale='en_US')['am'] + u'AM' + + :param width: the width to use, one of "abbreviated", "narrow", or "wide" + :param context: the context, either "format" or "stand-alone" + :param locale: the `Locale` object, or a locale string. Defaults to the system time locale. + """ + return Locale.parse(locale or LC_TIME).day_periods[context][width] + + +def get_day_names( + width: Literal['abbreviated', 'narrow', 'short', 'wide'] = 'wide', + context: _Context = 'format', + locale: Locale | str | None = None, +) -> LocaleDataDict: + """Return the day names used by the locale for the specified format. + + >>> get_day_names('wide', locale='en_US')[1] + u'Tuesday' + >>> get_day_names('short', locale='en_US')[1] + u'Tu' + >>> get_day_names('abbreviated', locale='es')[1] + u'mar' + >>> get_day_names('narrow', context='stand-alone', locale='de_DE')[1] + u'D' + + :param width: the width to use, one of "wide", "abbreviated", "short" or "narrow" + :param context: the context, either "format" or "stand-alone" + :param locale: the `Locale` object, or a locale string. Defaults to the system time locale. + """ + return Locale.parse(locale or LC_TIME).days[context][width] + + +def get_month_names( + width: Literal['abbreviated', 'narrow', 'wide'] = 'wide', + context: _Context = 'format', + locale: Locale | str | None = None, +) -> LocaleDataDict: + """Return the month names used by the locale for the specified format. + + >>> get_month_names('wide', locale='en_US')[1] + u'January' + >>> get_month_names('abbreviated', locale='es')[1] + u'ene' + >>> get_month_names('narrow', context='stand-alone', locale='de_DE')[1] + u'J' + + :param width: the width to use, one of "wide", "abbreviated", or "narrow" + :param context: the context, either "format" or "stand-alone" + :param locale: the `Locale` object, or a locale string. Defaults to the system time locale. + """ + return Locale.parse(locale or LC_TIME).months[context][width] + + +def get_quarter_names( + width: Literal['abbreviated', 'narrow', 'wide'] = 'wide', + context: _Context = 'format', + locale: Locale | str | None = None, +) -> LocaleDataDict: + """Return the quarter names used by the locale for the specified format. + + >>> get_quarter_names('wide', locale='en_US')[1] + u'1st quarter' + >>> get_quarter_names('abbreviated', locale='de_DE')[1] + u'Q1' + >>> get_quarter_names('narrow', locale='de_DE')[1] + u'1' + + :param width: the width to use, one of "wide", "abbreviated", or "narrow" + :param context: the context, either "format" or "stand-alone" + :param locale: the `Locale` object, or a locale string. Defaults to the system time locale. + """ + return Locale.parse(locale or LC_TIME).quarters[context][width] + + +def get_era_names( + width: Literal['abbreviated', 'narrow', 'wide'] = 'wide', + locale: Locale | str | None = None, +) -> LocaleDataDict: + """Return the era names used by the locale for the specified format. + + >>> get_era_names('wide', locale='en_US')[1] + u'Anno Domini' + >>> get_era_names('abbreviated', locale='de_DE')[1] + u'n. Chr.' + + :param width: the width to use, either "wide", "abbreviated", or "narrow" + :param locale: the `Locale` object, or a locale string. Defaults to the system time locale. + """ + return Locale.parse(locale or LC_TIME).eras[width] + + +def get_date_format( + format: _PredefinedTimeFormat = 'medium', + locale: Locale | str | None = None, +) -> DateTimePattern: + """Return the date formatting patterns used by the locale for the specified + format. + + >>> get_date_format(locale='en_US') + + >>> get_date_format('full', locale='de_DE') + + + :param format: the format to use, one of "full", "long", "medium", or + "short" + :param locale: the `Locale` object, or a locale string. Defaults to the system time locale. + """ + return Locale.parse(locale or LC_TIME).date_formats[format] + + +def get_datetime_format( + format: _PredefinedTimeFormat = 'medium', + locale: Locale | str | None = None, +) -> DateTimePattern: + """Return the datetime formatting patterns used by the locale for the + specified format. + + >>> get_datetime_format(locale='en_US') + u'{1}, {0}' + + :param format: the format to use, one of "full", "long", "medium", or + "short" + :param locale: the `Locale` object, or a locale string. Defaults to the system time locale. + """ + patterns = Locale.parse(locale or LC_TIME).datetime_formats + if format not in patterns: + format = None + return patterns[format] + + +def get_time_format( + format: _PredefinedTimeFormat = 'medium', + locale: Locale | str | None = None, +) -> DateTimePattern: + """Return the time formatting patterns used by the locale for the specified + format. + + >>> get_time_format(locale='en_US') + + >>> get_time_format('full', locale='de_DE') + + + :param format: the format to use, one of "full", "long", "medium", or + "short" + :param locale: the `Locale` object, or a locale string. Defaults to the system time locale. + """ + return Locale.parse(locale or LC_TIME).time_formats[format] + + +def get_timezone_gmt( + datetime: _Instant = None, + width: Literal['long', 'short', 'iso8601', 'iso8601_short'] = 'long', + locale: Locale | str | None = None, + return_z: bool = False, +) -> str: + """Return the timezone associated with the given `datetime` object formatted + as string indicating the offset from GMT. + + >>> from datetime import datetime + >>> dt = datetime(2007, 4, 1, 15, 30) + >>> get_timezone_gmt(dt, locale='en') + u'GMT+00:00' + >>> get_timezone_gmt(dt, locale='en', return_z=True) + 'Z' + >>> get_timezone_gmt(dt, locale='en', width='iso8601_short') + u'+00' + >>> tz = get_timezone('America/Los_Angeles') + >>> dt = _localize(tz, datetime(2007, 4, 1, 15, 30)) + >>> get_timezone_gmt(dt, locale='en') + u'GMT-07:00' + >>> get_timezone_gmt(dt, 'short', locale='en') + u'-0700' + >>> get_timezone_gmt(dt, locale='en', width='iso8601_short') + u'-07' + + The long format depends on the locale, for example in France the acronym + UTC string is used instead of GMT: + + >>> get_timezone_gmt(dt, 'long', locale='fr_FR') + u'UTC-07:00' + + .. versionadded:: 0.9 + + :param datetime: the ``datetime`` object; if `None`, the current date and + time in UTC is used + :param width: either "long" or "short" or "iso8601" or "iso8601_short" + :param locale: the `Locale` object, or a locale string. Defaults to the system time locale. + :param return_z: True or False; Function returns indicator "Z" + when local time offset is 0 + """ + datetime = _ensure_datetime_tzinfo(_get_datetime(datetime)) + locale = Locale.parse(locale or LC_TIME) + + offset = datetime.tzinfo.utcoffset(datetime) + seconds = offset.days * 24 * 60 * 60 + offset.seconds + hours, seconds = divmod(seconds, 3600) + if return_z and hours == 0 and seconds == 0: + return 'Z' + elif seconds == 0 and width == 'iso8601_short': + return '%+03d' % hours + elif width == 'short' or width == 'iso8601_short': + pattern = '%+03d%02d' + elif width == 'iso8601': + pattern = '%+03d:%02d' + else: + pattern = locale.zone_formats['gmt'] % '%+03d:%02d' + return pattern % (hours, seconds // 60) + + +def get_timezone_location( + dt_or_tzinfo: _DtOrTzinfo = None, + locale: Locale | str | None = None, + return_city: bool = False, +) -> str: + """Return a representation of the given timezone using "location format". + + The result depends on both the local display name of the country and the + city associated with the time zone: + + >>> tz = get_timezone('America/St_Johns') + >>> print(get_timezone_location(tz, locale='de_DE')) + Kanada (St. John’s) (Ortszeit) + >>> print(get_timezone_location(tz, locale='en')) + Canada (St. John’s) Time + >>> print(get_timezone_location(tz, locale='en', return_city=True)) + St. John’s + >>> tz = get_timezone('America/Mexico_City') + >>> get_timezone_location(tz, locale='de_DE') + u'Mexiko (Mexiko-Stadt) (Ortszeit)' + + If the timezone is associated with a country that uses only a single + timezone, just the localized country name is returned: + + >>> tz = get_timezone('Europe/Berlin') + >>> get_timezone_name(tz, locale='de_DE') + u'Mitteleurop\\xe4ische Zeit' + + .. versionadded:: 0.9 + + :param dt_or_tzinfo: the ``datetime`` or ``tzinfo`` object that determines + the timezone; if `None`, the current date and time in + UTC is assumed + :param locale: the `Locale` object, or a locale string. Defaults to the system time locale. + :param return_city: True or False, if True then return exemplar city (location) + for the time zone + :return: the localized timezone name using location format + + """ + locale = Locale.parse(locale or LC_TIME) + + zone = _get_tz_name(dt_or_tzinfo) + + # Get the canonical time-zone code + zone = get_global('zone_aliases').get(zone, zone) + + info = locale.time_zones.get(zone, {}) + + # Otherwise, if there is only one timezone for the country, return the + # localized country name + region_format = locale.zone_formats['region'] + territory = get_global('zone_territories').get(zone) + if territory not in locale.territories: + territory = 'ZZ' # invalid/unknown + territory_name = locale.territories[territory] + if not return_city and territory and len(get_global('territory_zones').get(territory, [])) == 1: + return region_format % territory_name + + # Otherwise, include the city in the output + fallback_format = locale.zone_formats['fallback'] + if 'city' in info: + city_name = info['city'] + else: + metazone = get_global('meta_zones').get(zone) + metazone_info = locale.meta_zones.get(metazone, {}) + if 'city' in metazone_info: + city_name = metazone_info['city'] + elif '/' in zone: + city_name = zone.split('/', 1)[1].replace('_', ' ') + else: + city_name = zone.replace('_', ' ') + + if return_city: + return city_name + return region_format % (fallback_format % { + '0': city_name, + '1': territory_name, + }) + + +def get_timezone_name( + dt_or_tzinfo: _DtOrTzinfo = None, + width: Literal['long', 'short'] = 'long', + uncommon: bool = False, + locale: Locale | str | None = None, + zone_variant: Literal['generic', 'daylight', 'standard'] | None = None, + return_zone: bool = False, +) -> str: + r"""Return the localized display name for the given timezone. The timezone + may be specified using a ``datetime`` or `tzinfo` object. + + >>> from datetime import time + >>> dt = time(15, 30, tzinfo=get_timezone('America/Los_Angeles')) + >>> get_timezone_name(dt, locale='en_US') # doctest: +SKIP + u'Pacific Standard Time' + >>> get_timezone_name(dt, locale='en_US', return_zone=True) + 'America/Los_Angeles' + >>> get_timezone_name(dt, width='short', locale='en_US') # doctest: +SKIP + u'PST' + + If this function gets passed only a `tzinfo` object and no concrete + `datetime`, the returned display name is independent of daylight savings + time. This can be used for example for selecting timezones, or to set the + time of events that recur across DST changes: + + >>> tz = get_timezone('America/Los_Angeles') + >>> get_timezone_name(tz, locale='en_US') + u'Pacific Time' + >>> get_timezone_name(tz, 'short', locale='en_US') + u'PT' + + If no localized display name for the timezone is available, and the timezone + is associated with a country that uses only a single timezone, the name of + that country is returned, formatted according to the locale: + + >>> tz = get_timezone('Europe/Berlin') + >>> get_timezone_name(tz, locale='de_DE') + u'Mitteleurop\xe4ische Zeit' + >>> get_timezone_name(tz, locale='pt_BR') + u'Hor\xe1rio da Europa Central' + + On the other hand, if the country uses multiple timezones, the city is also + included in the representation: + + >>> tz = get_timezone('America/St_Johns') + >>> get_timezone_name(tz, locale='de_DE') + u'Neufundland-Zeit' + + Note that short format is currently not supported for all timezones and + all locales. This is partially because not every timezone has a short + code in every locale. In that case it currently falls back to the long + format. + + For more information see `LDML Appendix J: Time Zone Display Names + `_ + + .. versionadded:: 0.9 + + .. versionchanged:: 1.0 + Added `zone_variant` support. + + :param dt_or_tzinfo: the ``datetime`` or ``tzinfo`` object that determines + the timezone; if a ``tzinfo`` object is used, the + resulting display name will be generic, i.e. + independent of daylight savings time; if `None`, the + current date in UTC is assumed + :param width: either "long" or "short" + :param uncommon: deprecated and ignored + :param zone_variant: defines the zone variation to return. By default the + variation is defined from the datetime object + passed in. If no datetime object is passed in, the + ``'generic'`` variation is assumed. The following + values are valid: ``'generic'``, ``'daylight'`` and + ``'standard'``. + :param locale: the `Locale` object, or a locale string. Defaults to the system time locale. + :param return_zone: True or False. If true then function + returns long time zone ID + """ + dt, tzinfo = _get_dt_and_tzinfo(dt_or_tzinfo) + locale = Locale.parse(locale or LC_TIME) + + zone = _get_tz_name(dt_or_tzinfo) + + if zone_variant is None: + if dt is None: + zone_variant = 'generic' + else: + dst = tzinfo.dst(dt) + zone_variant = "daylight" if dst else "standard" + else: + if zone_variant not in ('generic', 'standard', 'daylight'): + raise ValueError('Invalid zone variation') + + # Get the canonical time-zone code + zone = get_global('zone_aliases').get(zone, zone) + if return_zone: + return zone + info = locale.time_zones.get(zone, {}) + # Try explicitly translated zone names first + if width in info and zone_variant in info[width]: + return info[width][zone_variant] + + metazone = get_global('meta_zones').get(zone) + if metazone: + metazone_info = locale.meta_zones.get(metazone, {}) + if width in metazone_info: + name = metazone_info[width].get(zone_variant) + if width == 'short' and name == NO_INHERITANCE_MARKER: + # If the short form is marked no-inheritance, + # try to fall back to the long name instead. + name = metazone_info.get('long', {}).get(zone_variant) + if name: + return name + + # If we have a concrete datetime, we assume that the result can't be + # independent of daylight savings time, so we return the GMT offset + if dt is not None: + return get_timezone_gmt(dt, width=width, locale=locale) + + return get_timezone_location(dt_or_tzinfo, locale=locale) + + +def format_date( + date: datetime.date | None = None, + format: _PredefinedTimeFormat | str = 'medium', + locale: Locale | str | None = None, +) -> str: + """Return a date formatted according to the given pattern. + + >>> from datetime import date + >>> d = date(2007, 4, 1) + >>> format_date(d, locale='en_US') + u'Apr 1, 2007' + >>> format_date(d, format='full', locale='de_DE') + u'Sonntag, 1. April 2007' + + If you don't want to use the locale default formats, you can specify a + custom date pattern: + + >>> format_date(d, "EEE, MMM d, ''yy", locale='en') + u"Sun, Apr 1, '07" + + :param date: the ``date`` or ``datetime`` object; if `None`, the current + date is used + :param format: one of "full", "long", "medium", or "short", or a custom + date/time pattern + :param locale: a `Locale` object or a locale identifier. Defaults to the system time locale. + """ + if date is None: + date = datetime.date.today() + elif isinstance(date, datetime.datetime): + date = date.date() + + locale = Locale.parse(locale or LC_TIME) + if format in ('full', 'long', 'medium', 'short'): + format = get_date_format(format, locale=locale) + pattern = parse_pattern(format) + return pattern.apply(date, locale) + + +def format_datetime( + datetime: _Instant = None, + format: _PredefinedTimeFormat | str = 'medium', + tzinfo: datetime.tzinfo | None = None, + locale: Locale | str | None = None, +) -> str: + r"""Return a date formatted according to the given pattern. + + >>> from datetime import datetime + >>> dt = datetime(2007, 4, 1, 15, 30) + >>> format_datetime(dt, locale='en_US') + u'Apr 1, 2007, 3:30:00\u202fPM' + + For any pattern requiring the display of the timezone: + + >>> format_datetime(dt, 'full', tzinfo=get_timezone('Europe/Paris'), + ... locale='fr_FR') + 'dimanche 1 avril 2007, 17:30:00 heure d’été d’Europe centrale' + >>> format_datetime(dt, "yyyy.MM.dd G 'at' HH:mm:ss zzz", + ... tzinfo=get_timezone('US/Eastern'), locale='en') + u'2007.04.01 AD at 11:30:00 EDT' + + :param datetime: the `datetime` object; if `None`, the current date and + time is used + :param format: one of "full", "long", "medium", or "short", or a custom + date/time pattern + :param tzinfo: the timezone to apply to the time for display + :param locale: a `Locale` object or a locale identifier. Defaults to the system time locale. + """ + datetime = _ensure_datetime_tzinfo(_get_datetime(datetime), tzinfo) + + locale = Locale.parse(locale or LC_TIME) + if format in ('full', 'long', 'medium', 'short'): + return get_datetime_format(format, locale=locale) \ + .replace("'", "") \ + .replace('{0}', format_time(datetime, format, tzinfo=None, + locale=locale)) \ + .replace('{1}', format_date(datetime, format, locale=locale)) + else: + return parse_pattern(format).apply(datetime, locale) + + +def format_time( + time: datetime.time | datetime.datetime | float | None = None, + format: _PredefinedTimeFormat | str = 'medium', + tzinfo: datetime.tzinfo | None = None, + locale: Locale | str | None = None, +) -> str: + r"""Return a time formatted according to the given pattern. + + >>> from datetime import datetime, time + >>> t = time(15, 30) + >>> format_time(t, locale='en_US') + u'3:30:00\u202fPM' + >>> format_time(t, format='short', locale='de_DE') + u'15:30' + + If you don't want to use the locale default formats, you can specify a + custom time pattern: + + >>> format_time(t, "hh 'o''clock' a", locale='en') + u"03 o'clock PM" + + For any pattern requiring the display of the time-zone a + timezone has to be specified explicitly: + + >>> t = datetime(2007, 4, 1, 15, 30) + >>> tzinfo = get_timezone('Europe/Paris') + >>> t = _localize(tzinfo, t) + >>> format_time(t, format='full', tzinfo=tzinfo, locale='fr_FR') + '15:30:00 heure d’été d’Europe centrale' + >>> format_time(t, "hh 'o''clock' a, zzzz", tzinfo=get_timezone('US/Eastern'), + ... locale='en') + u"09 o'clock AM, Eastern Daylight Time" + + As that example shows, when this function gets passed a + ``datetime.datetime`` value, the actual time in the formatted string is + adjusted to the timezone specified by the `tzinfo` parameter. If the + ``datetime`` is "naive" (i.e. it has no associated timezone information), + it is assumed to be in UTC. + + These timezone calculations are **not** performed if the value is of type + ``datetime.time``, as without date information there's no way to determine + what a given time would translate to in a different timezone without + information about whether daylight savings time is in effect or not. This + means that time values are left as-is, and the value of the `tzinfo` + parameter is only used to display the timezone name if needed: + + >>> t = time(15, 30) + >>> format_time(t, format='full', tzinfo=get_timezone('Europe/Paris'), + ... locale='fr_FR') # doctest: +SKIP + u'15:30:00 heure normale d\u2019Europe centrale' + >>> format_time(t, format='full', tzinfo=get_timezone('US/Eastern'), + ... locale='en_US') # doctest: +SKIP + u'3:30:00\u202fPM Eastern Standard Time' + + :param time: the ``time`` or ``datetime`` object; if `None`, the current + time in UTC is used + :param format: one of "full", "long", "medium", or "short", or a custom + date/time pattern + :param tzinfo: the time-zone to apply to the time for display + :param locale: a `Locale` object or a locale identifier. Defaults to the system time locale. + """ + + # get reference date for if we need to find the right timezone variant + # in the pattern + ref_date = time.date() if isinstance(time, datetime.datetime) else None + + time = _get_time(time, tzinfo) + + locale = Locale.parse(locale or LC_TIME) + if format in ('full', 'long', 'medium', 'short'): + format = get_time_format(format, locale=locale) + return parse_pattern(format).apply(time, locale, reference_date=ref_date) + + +def format_skeleton( + skeleton: str, + datetime: _Instant = None, + tzinfo: datetime.tzinfo | None = None, + fuzzy: bool = True, + locale: Locale | str | None = None, +) -> str: + r"""Return a time and/or date formatted according to the given pattern. + + The skeletons are defined in the CLDR data and provide more flexibility + than the simple short/long/medium formats, but are a bit harder to use. + The are defined using the date/time symbols without order or punctuation + and map to a suitable format for the given locale. + + >>> from datetime import datetime + >>> t = datetime(2007, 4, 1, 15, 30) + >>> format_skeleton('MMMEd', t, locale='fr') + u'dim. 1 avr.' + >>> format_skeleton('MMMEd', t, locale='en') + u'Sun, Apr 1' + >>> format_skeleton('yMMd', t, locale='fi') # yMMd is not in the Finnish locale; yMd gets used + u'1.4.2007' + >>> format_skeleton('yMMd', t, fuzzy=False, locale='fi') # yMMd is not in the Finnish locale, an error is thrown + Traceback (most recent call last): + ... + KeyError: yMMd + >>> format_skeleton('GH', t, fuzzy=True, locale='fi_FI') # GH is not in the Finnish locale and there is no close match, an error is thrown + Traceback (most recent call last): + ... + KeyError: None + + After the skeleton is resolved to a pattern `format_datetime` is called so + all timezone processing etc is the same as for that. + + :param skeleton: A date time skeleton as defined in the cldr data. + :param datetime: the ``time`` or ``datetime`` object; if `None`, the current + time in UTC is used + :param tzinfo: the time-zone to apply to the time for display + :param fuzzy: If the skeleton is not found, allow choosing a skeleton that's + close enough to it. If there is no close match, a `KeyError` + is thrown. + :param locale: a `Locale` object or a locale identifier. Defaults to the system time locale. + """ + locale = Locale.parse(locale or LC_TIME) + if fuzzy and skeleton not in locale.datetime_skeletons: + skeleton = match_skeleton(skeleton, locale.datetime_skeletons) + format = locale.datetime_skeletons[skeleton] + return format_datetime(datetime, format, tzinfo, locale) + + +TIMEDELTA_UNITS: tuple[tuple[str, int], ...] = ( + ('year', 3600 * 24 * 365), + ('month', 3600 * 24 * 30), + ('week', 3600 * 24 * 7), + ('day', 3600 * 24), + ('hour', 3600), + ('minute', 60), + ('second', 1), +) + + +def format_timedelta( + delta: datetime.timedelta | int, + granularity: Literal['year', 'month', 'week', 'day', 'hour', 'minute', 'second'] = 'second', + threshold: float = .85, + add_direction: bool = False, + format: Literal['narrow', 'short', 'medium', 'long'] = 'long', + locale: Locale | str | None = None, +) -> str: + """Return a time delta according to the rules of the given locale. + + >>> from datetime import timedelta + >>> format_timedelta(timedelta(weeks=12), locale='en_US') + u'3 months' + >>> format_timedelta(timedelta(seconds=1), locale='es') + u'1 segundo' + + The granularity parameter can be provided to alter the lowest unit + presented, which defaults to a second. + + >>> format_timedelta(timedelta(hours=3), granularity='day', locale='en_US') + u'1 day' + + The threshold parameter can be used to determine at which value the + presentation switches to the next higher unit. A higher threshold factor + means the presentation will switch later. For example: + + >>> format_timedelta(timedelta(hours=23), threshold=0.9, locale='en_US') + u'1 day' + >>> format_timedelta(timedelta(hours=23), threshold=1.1, locale='en_US') + u'23 hours' + + In addition directional information can be provided that informs + the user if the date is in the past or in the future: + + >>> format_timedelta(timedelta(hours=1), add_direction=True, locale='en') + u'in 1 hour' + >>> format_timedelta(timedelta(hours=-1), add_direction=True, locale='en') + u'1 hour ago' + + The format parameter controls how compact or wide the presentation is: + + >>> format_timedelta(timedelta(hours=3), format='short', locale='en') + u'3 hr' + >>> format_timedelta(timedelta(hours=3), format='narrow', locale='en') + u'3h' + + :param delta: a ``timedelta`` object representing the time difference to + format, or the delta in seconds as an `int` value + :param granularity: determines the smallest unit that should be displayed, + the value can be one of "year", "month", "week", "day", + "hour", "minute" or "second" + :param threshold: factor that determines at which point the presentation + switches to the next higher unit + :param add_direction: if this flag is set to `True` the return value will + include directional information. For instance a + positive timedelta will include the information about + it being in the future, a negative will be information + about the value being in the past. + :param format: the format, can be "narrow", "short" or "long". ( + "medium" is deprecated, currently converted to "long" to + maintain compatibility) + :param locale: a `Locale` object or a locale identifier. Defaults to the system time locale. + """ + if format not in ('narrow', 'short', 'medium', 'long'): + raise TypeError('Format must be one of "narrow", "short" or "long"') + if format == 'medium': + warnings.warn( + '"medium" value for format param of format_timedelta' + ' is deprecated. Use "long" instead', + category=DeprecationWarning, + stacklevel=2, + ) + format = 'long' + if isinstance(delta, datetime.timedelta): + seconds = int((delta.days * 86400) + delta.seconds) + else: + seconds = delta + locale = Locale.parse(locale or LC_TIME) + date_fields = locale._data["date_fields"] + unit_patterns = locale._data["unit_patterns"] + + def _iter_patterns(a_unit): + if add_direction: + # Try to find the length variant version first ("year-narrow") + # before falling back to the default. + unit_rel_patterns = (date_fields.get(f"{a_unit}-{format}") or date_fields[a_unit]) + if seconds >= 0: + yield unit_rel_patterns['future'] + else: + yield unit_rel_patterns['past'] + a_unit = f"duration-{a_unit}" + unit_pats = unit_patterns.get(a_unit, {}) + yield unit_pats.get(format) + # We do not support `` tags at all while ingesting CLDR data, + # so these aliases specified in `root.xml` are hard-coded here: + # + # + if format in ("long", "narrow"): + yield unit_pats.get("short") + + for unit, secs_per_unit in TIMEDELTA_UNITS: + value = abs(seconds) / secs_per_unit + if value >= threshold or unit == granularity: + if unit == granularity and value > 0: + value = max(1, value) + value = int(round(value)) + plural_form = locale.plural_form(value) + pattern = None + for patterns in _iter_patterns(unit): + if patterns is not None: + pattern = patterns.get(plural_form) or patterns.get('other') + if pattern: + break + # This really should not happen + if pattern is None: + return '' + return pattern.replace('{0}', str(value)) + + return '' + + +def _format_fallback_interval( + start: _Instant, + end: _Instant, + skeleton: str | None, + tzinfo: datetime.tzinfo | None, + locale: Locale, +) -> str: + if skeleton in locale.datetime_skeletons: # Use the given skeleton + format = lambda dt: format_skeleton(skeleton, dt, tzinfo, locale=locale) + elif all((isinstance(d, datetime.date) and not isinstance(d, datetime.datetime)) for d in (start, end)): # Both are just dates + format = lambda dt: format_date(dt, locale=locale) + elif all((isinstance(d, datetime.time) and not isinstance(d, datetime.date)) for d in (start, end)): # Both are times + format = lambda dt: format_time(dt, tzinfo=tzinfo, locale=locale) + else: + format = lambda dt: format_datetime(dt, tzinfo=tzinfo, locale=locale) + + formatted_start = format(start) + formatted_end = format(end) + + if formatted_start == formatted_end: + return format(start) + + return ( + locale.interval_formats.get(None, "{0}-{1}"). + replace("{0}", formatted_start). + replace("{1}", formatted_end) + ) + + +def format_interval( + start: _Instant, + end: _Instant, + skeleton: str | None = None, + tzinfo: datetime.tzinfo | None = None, + fuzzy: bool = True, + locale: Locale | str | None = None, +) -> str: + """ + Format an interval between two instants according to the locale's rules. + + >>> from datetime import date, time + >>> format_interval(date(2016, 1, 15), date(2016, 1, 17), "yMd", locale="fi") + u'15.\u201317.1.2016' + + >>> format_interval(time(12, 12), time(16, 16), "Hm", locale="en_GB") + '12:12\u201316:16' + + >>> format_interval(time(5, 12), time(16, 16), "hm", locale="en_US") + '5:12\u202fAM\u2009–\u20094:16\u202fPM' + + >>> format_interval(time(16, 18), time(16, 24), "Hm", locale="it") + '16:18\u201316:24' + + If the start instant equals the end instant, the interval is formatted like the instant. + + >>> format_interval(time(16, 18), time(16, 18), "Hm", locale="it") + '16:18' + + Unknown skeletons fall back to "default" formatting. + + >>> format_interval(date(2015, 1, 1), date(2017, 1, 1), "wzq", locale="ja") + '2015/01/01\uff5e2017/01/01' + + >>> format_interval(time(16, 18), time(16, 24), "xxx", locale="ja") + '16:18:00\uff5e16:24:00' + + >>> format_interval(date(2016, 1, 15), date(2016, 1, 17), "xxx", locale="de") + '15.01.2016\u2009–\u200917.01.2016' + + :param start: First instant (datetime/date/time) + :param end: Second instant (datetime/date/time) + :param skeleton: The "skeleton format" to use for formatting. + :param tzinfo: tzinfo to use (if none is already attached) + :param fuzzy: If the skeleton is not found, allow choosing a skeleton that's + close enough to it. + :param locale: A locale object or identifier. Defaults to the system time locale. + :return: Formatted interval + """ + locale = Locale.parse(locale or LC_TIME) + + # NB: The quote comments below are from the algorithm description in + # https://www.unicode.org/reports/tr35/tr35-dates.html#intervalFormats + + # > Look for the intervalFormatItem element that matches the "skeleton", + # > starting in the current locale and then following the locale fallback + # > chain up to, but not including root. + + interval_formats = locale.interval_formats + + if skeleton not in interval_formats or not skeleton: + # > If no match was found from the previous step, check what the closest + # > match is in the fallback locale chain, as in availableFormats. That + # > is, this allows for adjusting the string value field's width, + # > including adjusting between "MMM" and "MMMM", and using different + # > variants of the same field, such as 'v' and 'z'. + if skeleton and fuzzy: + skeleton = match_skeleton(skeleton, interval_formats) + else: + skeleton = None + if not skeleton: # Still no match whatsoever? + # > Otherwise, format the start and end datetime using the fallback pattern. + return _format_fallback_interval(start, end, skeleton, tzinfo, locale) + + skel_formats = interval_formats[skeleton] + + if start == end: + return format_skeleton(skeleton, start, tzinfo, fuzzy=fuzzy, locale=locale) + + start = _ensure_datetime_tzinfo(_get_datetime(start), tzinfo=tzinfo) + end = _ensure_datetime_tzinfo(_get_datetime(end), tzinfo=tzinfo) + + start_fmt = DateTimeFormat(start, locale=locale) + end_fmt = DateTimeFormat(end, locale=locale) + + # > If a match is found from previous steps, compute the calendar field + # > with the greatest difference between start and end datetime. If there + # > is no difference among any of the fields in the pattern, format as a + # > single date using availableFormats, and return. + + for field in PATTERN_CHAR_ORDER: # These are in largest-to-smallest order + if field in skel_formats and start_fmt.extract(field) != end_fmt.extract(field): + # > If there is a match, use the pieces of the corresponding pattern to + # > format the start and end datetime, as above. + return "".join( + parse_pattern(pattern).apply(instant, locale) + for pattern, instant + in zip(skel_formats[field], (start, end)) + ) + + # > Otherwise, format the start and end datetime using the fallback pattern. + + return _format_fallback_interval(start, end, skeleton, tzinfo, locale) + + +def get_period_id( + time: _Instant, + tzinfo: datetime.tzinfo | None = None, + type: Literal['selection'] | None = None, + locale: Locale | str | None = None, +) -> str: + """ + Get the day period ID for a given time. + + This ID can be used as a key for the period name dictionary. + + >>> from datetime import time + >>> get_period_names(locale="de")[get_period_id(time(7, 42), locale="de")] + u'Morgen' + + >>> get_period_id(time(0), locale="en_US") + u'midnight' + + >>> get_period_id(time(0), type="selection", locale="en_US") + u'night1' + + :param time: The time to inspect. + :param tzinfo: The timezone for the time. See ``format_time``. + :param type: The period type to use. Either "selection" or None. + The selection type is used for selecting among phrases such as + “Your email arrived yesterday evening” or “Your email arrived last night”. + :param locale: the `Locale` object, or a locale string. Defaults to the system time locale. + :return: period ID. Something is always returned -- even if it's just "am" or "pm". + """ + time = _get_time(time, tzinfo) + seconds_past_midnight = int(time.hour * 60 * 60 + time.minute * 60 + time.second) + locale = Locale.parse(locale or LC_TIME) + + # The LDML rules state that the rules may not overlap, so iterating in arbitrary + # order should be alright, though `at` periods should be preferred. + rulesets = locale.day_period_rules.get(type, {}).items() + + for rule_id, rules in rulesets: + for rule in rules: + if "at" in rule and rule["at"] == seconds_past_midnight: + return rule_id + + for rule_id, rules in rulesets: + for rule in rules: + if "from" in rule and "before" in rule: + if rule["from"] < rule["before"]: + if rule["from"] <= seconds_past_midnight < rule["before"]: + return rule_id + else: + # e.g. from="21:00" before="06:00" + if rule["from"] <= seconds_past_midnight < 86400 or \ + 0 <= seconds_past_midnight < rule["before"]: + return rule_id + + start_ok = end_ok = False + + if "from" in rule and seconds_past_midnight >= rule["from"]: + start_ok = True + if "to" in rule and seconds_past_midnight <= rule["to"]: + # This rule type does not exist in the present CLDR data; + # excuse the lack of test coverage. + end_ok = True + if "before" in rule and seconds_past_midnight < rule["before"]: + end_ok = True + if "after" in rule: + raise NotImplementedError("'after' is deprecated as of CLDR 29.") + + if start_ok and end_ok: + return rule_id + + if seconds_past_midnight < 43200: + return "am" + else: + return "pm" + + +class ParseError(ValueError): + pass + + +def parse_date( + string: str, + locale: Locale | str | None = None, + format: _PredefinedTimeFormat | str = 'medium', +) -> datetime.date: + """Parse a date from a string. + + If an explicit format is provided, it is used to parse the date. + + >>> parse_date('01.04.2004', format='dd.MM.yyyy') + datetime.date(2004, 4, 1) + + If no format is given, or if it is one of "full", "long", "medium", + or "short", the function first tries to interpret the string as + ISO-8601 date format and then uses the date format for the locale + as a hint to determine the order in which the date fields appear in + the string. + + >>> parse_date('4/1/04', locale='en_US') + datetime.date(2004, 4, 1) + >>> parse_date('01.04.2004', locale='de_DE') + datetime.date(2004, 4, 1) + >>> parse_date('2004-04-01', locale='en_US') + datetime.date(2004, 4, 1) + >>> parse_date('2004-04-01', locale='de_DE') + datetime.date(2004, 4, 1) + >>> parse_date('01.04.04', locale='de_DE', format='short') + datetime.date(2004, 4, 1) + + :param string: the string containing the date + :param locale: a `Locale` object or a locale identifier + :param locale: a `Locale` object or a locale identifier. Defaults to the system time locale. + :param format: the format to use, either an explicit date format, + or one of "full", "long", "medium", or "short" + (see ``get_time_format``) + """ + numbers = re.findall(r'(\d+)', string) + if not numbers: + raise ParseError("No numbers were found in input") + + use_predefined_format = format in ('full', 'long', 'medium', 'short') + # we try ISO-8601 format first, meaning similar to formats + # extended YYYY-MM-DD or basic YYYYMMDD + iso_alike = re.match(r'^(\d{4})-?([01]\d)-?([0-3]\d)$', + string, flags=re.ASCII) # allow only ASCII digits + if iso_alike and use_predefined_format: + try: + return datetime.date(*map(int, iso_alike.groups())) + except ValueError: + pass # a locale format might fit better, so let's continue + + if use_predefined_format: + fmt = get_date_format(format=format, locale=locale) + else: + fmt = parse_pattern(format) + format_str = fmt.pattern.lower() + year_idx = format_str.index('y') + month_idx = format_str.find('m') + if month_idx < 0: + month_idx = format_str.index('l') + day_idx = format_str.index('d') + + indexes = sorted([(year_idx, 'Y'), (month_idx, 'M'), (day_idx, 'D')]) + indexes = {item[1]: idx for idx, item in enumerate(indexes)} + + # FIXME: this currently only supports numbers, but should also support month + # names, both in the requested locale, and english + + year = numbers[indexes['Y']] + year = 2000 + int(year) if len(year) == 2 else int(year) + month = int(numbers[indexes['M']]) + day = int(numbers[indexes['D']]) + if month > 12: + month, day = day, month + return datetime.date(year, month, day) + + +def parse_time( + string: str, + locale: Locale | str | None = None, + format: _PredefinedTimeFormat | str = 'medium', +) -> datetime.time: + """Parse a time from a string. + + This function uses the time format for the locale as a hint to determine + the order in which the time fields appear in the string. + + If an explicit format is provided, the function will use it to parse + the time instead. + + >>> parse_time('15:30:00', locale='en_US') + datetime.time(15, 30) + >>> parse_time('15:30:00', format='H:mm:ss') + datetime.time(15, 30) + + :param string: the string containing the time + :param locale: a `Locale` object or a locale identifier. Defaults to the system time locale. + :param format: the format to use, either an explicit time format, + or one of "full", "long", "medium", or "short" + (see ``get_time_format``) + :return: the parsed time + :rtype: `time` + """ + numbers = re.findall(r'(\d+)', string) + if not numbers: + raise ParseError("No numbers were found in input") + + # TODO: try ISO format first? + if format in ('full', 'long', 'medium', 'short'): + fmt = get_time_format(format=format, locale=locale) + else: + fmt = parse_pattern(format) + format_str = fmt.pattern.lower() + hour_idx = format_str.find('h') + if hour_idx < 0: + hour_idx = format_str.index('k') + min_idx = format_str.index('m') + # format might not contain seconds + if (sec_idx := format_str.find('s')) < 0: + sec_idx = math.inf + + indexes = sorted([(hour_idx, 'H'), (min_idx, 'M'), (sec_idx, 'S')]) + indexes = {item[1]: idx for idx, item in enumerate(indexes)} + + # TODO: support time zones + + # Check if the format specifies a period to be used; + # if it does, look for 'pm' to figure out an offset. + hour_offset = 0 + if 'a' in format_str and 'pm' in string.lower(): + hour_offset = 12 + + # Parse up to three numbers from the string. + minute = second = 0 + hour = int(numbers[indexes['H']]) + hour_offset + if len(numbers) > 1: + minute = int(numbers[indexes['M']]) + if len(numbers) > 2: + second = int(numbers[indexes['S']]) + return datetime.time(hour, minute, second) + + +class DateTimePattern: + + def __init__(self, pattern: str, format: DateTimeFormat): + self.pattern = pattern + self.format = format + + def __repr__(self) -> str: + return f"<{type(self).__name__} {self.pattern!r}>" + + def __str__(self) -> str: + pat = self.pattern + return pat + + def __mod__(self, other: DateTimeFormat) -> str: + if not isinstance(other, DateTimeFormat): + return NotImplemented + return self.format % other + + def apply( + self, + datetime: datetime.date | datetime.time, + locale: Locale | str | None, + reference_date: datetime.date | None = None, + ) -> str: + return self % DateTimeFormat(datetime, locale, reference_date) + + +class DateTimeFormat: + + def __init__( + self, + value: datetime.date | datetime.time, + locale: Locale | str, + reference_date: datetime.date | None = None, + ) -> None: + assert isinstance(value, (datetime.date, datetime.datetime, datetime.time)) + if isinstance(value, (datetime.datetime, datetime.time)) and value.tzinfo is None: + value = value.replace(tzinfo=UTC) + self.value = value + self.locale = Locale.parse(locale) + self.reference_date = reference_date + + def __getitem__(self, name: str) -> str: + char = name[0] + num = len(name) + if char == 'G': + return self.format_era(char, num) + elif char in ('y', 'Y', 'u'): + return self.format_year(char, num) + elif char in ('Q', 'q'): + return self.format_quarter(char, num) + elif char in ('M', 'L'): + return self.format_month(char, num) + elif char in ('w', 'W'): + return self.format_week(char, num) + elif char == 'd': + return self.format(self.value.day, num) + elif char == 'D': + return self.format_day_of_year(num) + elif char == 'F': + return self.format_day_of_week_in_month() + elif char in ('E', 'e', 'c'): + return self.format_weekday(char, num) + elif char in ('a', 'b', 'B'): + return self.format_period(char, num) + elif char == 'h': + if self.value.hour % 12 == 0: + return self.format(12, num) + else: + return self.format(self.value.hour % 12, num) + elif char == 'H': + return self.format(self.value.hour, num) + elif char == 'K': + return self.format(self.value.hour % 12, num) + elif char == 'k': + if self.value.hour == 0: + return self.format(24, num) + else: + return self.format(self.value.hour, num) + elif char == 'm': + return self.format(self.value.minute, num) + elif char == 's': + return self.format(self.value.second, num) + elif char == 'S': + return self.format_frac_seconds(num) + elif char == 'A': + return self.format_milliseconds_in_day(num) + elif char in ('z', 'Z', 'v', 'V', 'x', 'X', 'O'): + return self.format_timezone(char, num) + else: + raise KeyError(f"Unsupported date/time field {char!r}") + + def extract(self, char: str) -> int: + char = str(char)[0] + if char == 'y': + return self.value.year + elif char == 'M': + return self.value.month + elif char == 'd': + return self.value.day + elif char == 'H': + return self.value.hour + elif char == 'h': + return self.value.hour % 12 or 12 + elif char == 'm': + return self.value.minute + elif char == 'a': + return int(self.value.hour >= 12) # 0 for am, 1 for pm + else: + raise NotImplementedError(f"Not implemented: extracting {char!r} from {self.value!r}") + + def format_era(self, char: str, num: int) -> str: + width = {3: 'abbreviated', 4: 'wide', 5: 'narrow'}[max(3, num)] + era = int(self.value.year >= 0) + return get_era_names(width, self.locale)[era] + + def format_year(self, char: str, num: int) -> str: + value = self.value.year + if char.isupper(): + month = self.value.month + if month == 1 and self.value.day < 7 and self.get_week_of_year() >= 52: + value -= 1 + elif month == 12 and self.value.day > 25 and self.get_week_of_year() <= 2: + value += 1 + year = self.format(value, num) + if num == 2: + year = year[-2:] + return year + + def format_quarter(self, char: str, num: int) -> str: + quarter = (self.value.month - 1) // 3 + 1 + if num <= 2: + return '%0*d' % (num, quarter) + width = {3: 'abbreviated', 4: 'wide', 5: 'narrow'}[num] + context = {'Q': 'format', 'q': 'stand-alone'}[char] + return get_quarter_names(width, context, self.locale)[quarter] + + def format_month(self, char: str, num: int) -> str: + if num <= 2: + return '%0*d' % (num, self.value.month) + width = {3: 'abbreviated', 4: 'wide', 5: 'narrow'}[num] + context = {'M': 'format', 'L': 'stand-alone'}[char] + return get_month_names(width, context, self.locale)[self.value.month] + + def format_week(self, char: str, num: int) -> str: + if char.islower(): # week of year + week = self.get_week_of_year() + return self.format(week, num) + else: # week of month + week = self.get_week_of_month() + return str(week) + + def format_weekday(self, char: str = 'E', num: int = 4) -> str: + """ + Return weekday from parsed datetime according to format pattern. + + >>> from datetime import date + >>> format = DateTimeFormat(date(2016, 2, 28), Locale.parse('en_US')) + >>> format.format_weekday() + u'Sunday' + + 'E': Day of week - Use one through three letters for the abbreviated day name, four for the full (wide) name, + five for the narrow name, or six for the short name. + >>> format.format_weekday('E',2) + u'Sun' + + 'e': Local day of week. Same as E except adds a numeric value that will depend on the local starting day of the + week, using one or two letters. For this example, Monday is the first day of the week. + >>> format.format_weekday('e',2) + '01' + + 'c': Stand-Alone local day of week - Use one letter for the local numeric value (same as 'e'), three for the + abbreviated day name, four for the full (wide) name, five for the narrow name, or six for the short name. + >>> format.format_weekday('c',1) + '1' + + :param char: pattern format character ('e','E','c') + :param num: count of format character + + """ + if num < 3: + if char.islower(): + value = 7 - self.locale.first_week_day + self.value.weekday() + return self.format(value % 7 + 1, num) + num = 3 + weekday = self.value.weekday() + width = {3: 'abbreviated', 4: 'wide', 5: 'narrow', 6: 'short'}[num] + context = "stand-alone" if char == "c" else "format" + return get_day_names(width, context, self.locale)[weekday] + + def format_day_of_year(self, num: int) -> str: + return self.format(self.get_day_of_year(), num) + + def format_day_of_week_in_month(self) -> str: + return str((self.value.day - 1) // 7 + 1) + + def format_period(self, char: str, num: int) -> str: + """ + Return period from parsed datetime according to format pattern. + + >>> from datetime import datetime, time + >>> format = DateTimeFormat(time(13, 42), 'fi_FI') + >>> format.format_period('a', 1) + u'ip.' + >>> format.format_period('b', 1) + u'iltap.' + >>> format.format_period('b', 4) + u'iltapäivä' + >>> format.format_period('B', 4) + u'iltapäivällä' + >>> format.format_period('B', 5) + u'ip.' + + >>> format = DateTimeFormat(datetime(2022, 4, 28, 6, 27), 'zh_Hant') + >>> format.format_period('a', 1) + u'上午' + >>> format.format_period('B', 1) + u'清晨' + + :param char: pattern format character ('a', 'b', 'B') + :param num: count of format character + + """ + widths = [{3: 'abbreviated', 4: 'wide', 5: 'narrow'}[max(3, num)], + 'wide', 'narrow', 'abbreviated'] + if char == 'a': + period = 'pm' if self.value.hour >= 12 else 'am' + context = 'format' + else: + period = get_period_id(self.value, locale=self.locale) + context = 'format' if char == 'B' else 'stand-alone' + for width in widths: + period_names = get_period_names(context=context, width=width, locale=self.locale) + if period in period_names: + return period_names[period] + raise ValueError(f"Could not format period {period} in {self.locale}") + + def format_frac_seconds(self, num: int) -> str: + """ Return fractional seconds. + + Rounds the time's microseconds to the precision given by the number \ + of digits passed in. + """ + value = self.value.microsecond / 1000000 + return self.format(round(value, num) * 10**num, num) + + def format_milliseconds_in_day(self, num): + msecs = self.value.microsecond // 1000 + self.value.second * 1000 + \ + self.value.minute * 60000 + self.value.hour * 3600000 + return self.format(msecs, num) + + def format_timezone(self, char: str, num: int) -> str: + width = {3: 'short', 4: 'long', 5: 'iso8601'}[max(3, num)] + + # It could be that we only receive a time to format, but also have a + # reference date which is important to distinguish between timezone + # variants (summer/standard time) + value = self.value + if self.reference_date: + value = datetime.datetime.combine(self.reference_date, self.value) + + if char == 'z': + return get_timezone_name(value, width, locale=self.locale) + elif char == 'Z': + if num == 5: + return get_timezone_gmt(value, width, locale=self.locale, return_z=True) + return get_timezone_gmt(value, width, locale=self.locale) + elif char == 'O': + if num == 4: + return get_timezone_gmt(value, width, locale=self.locale) + # TODO: To add support for O:1 + elif char == 'v': + return get_timezone_name(value.tzinfo, width, + locale=self.locale) + elif char == 'V': + if num == 1: + return get_timezone_name(value.tzinfo, width, + uncommon=True, locale=self.locale) + elif num == 2: + return get_timezone_name(value.tzinfo, locale=self.locale, return_zone=True) + elif num == 3: + return get_timezone_location(value.tzinfo, locale=self.locale, return_city=True) + return get_timezone_location(value.tzinfo, locale=self.locale) + # Included additional elif condition to add support for 'Xx' in timezone format + elif char == 'X': + if num == 1: + return get_timezone_gmt(value, width='iso8601_short', locale=self.locale, + return_z=True) + elif num in (2, 4): + return get_timezone_gmt(value, width='short', locale=self.locale, + return_z=True) + elif num in (3, 5): + return get_timezone_gmt(value, width='iso8601', locale=self.locale, + return_z=True) + elif char == 'x': + if num == 1: + return get_timezone_gmt(value, width='iso8601_short', locale=self.locale) + elif num in (2, 4): + return get_timezone_gmt(value, width='short', locale=self.locale) + elif num in (3, 5): + return get_timezone_gmt(value, width='iso8601', locale=self.locale) + + def format(self, value: SupportsInt, length: int) -> str: + return '%0*d' % (length, value) + + def get_day_of_year(self, date: datetime.date | None = None) -> int: + if date is None: + date = self.value + return (date - date.replace(month=1, day=1)).days + 1 + + def get_week_of_year(self) -> int: + """Return the week of the year.""" + day_of_year = self.get_day_of_year(self.value) + week = self.get_week_number(day_of_year) + if week == 0: + date = datetime.date(self.value.year - 1, 12, 31) + week = self.get_week_number(self.get_day_of_year(date), + date.weekday()) + elif week > 52: + weekday = datetime.date(self.value.year + 1, 1, 1).weekday() + if self.get_week_number(1, weekday) == 1 and \ + 32 - (weekday - self.locale.first_week_day) % 7 <= self.value.day: + week = 1 + return week + + def get_week_of_month(self) -> int: + """Return the week of the month.""" + return self.get_week_number(self.value.day) + + def get_week_number(self, day_of_period: int, day_of_week: int | None = None) -> int: + """Return the number of the week of a day within a period. This may be + the week number in a year or the week number in a month. + + Usually this will return a value equal to or greater than 1, but if the + first week of the period is so short that it actually counts as the last + week of the previous period, this function will return 0. + + >>> date = datetime.date(2006, 1, 8) + >>> DateTimeFormat(date, 'de_DE').get_week_number(6) + 1 + >>> DateTimeFormat(date, 'en_US').get_week_number(6) + 2 + + :param day_of_period: the number of the day in the period (usually + either the day of month or the day of year) + :param day_of_week: the week day; if omitted, the week day of the + current date is assumed + """ + if day_of_week is None: + day_of_week = self.value.weekday() + first_day = (day_of_week - self.locale.first_week_day - + day_of_period + 1) % 7 + if first_day < 0: + first_day += 7 + week_number = (day_of_period + first_day - 1) // 7 + if 7 - first_day >= self.locale.min_week_days: + week_number += 1 + return week_number + + +PATTERN_CHARS: dict[str, list[int] | None] = { + 'G': [1, 2, 3, 4, 5], # era + 'y': None, 'Y': None, 'u': None, # year + 'Q': [1, 2, 3, 4, 5], 'q': [1, 2, 3, 4, 5], # quarter + 'M': [1, 2, 3, 4, 5], 'L': [1, 2, 3, 4, 5], # month + 'w': [1, 2], 'W': [1], # week + 'd': [1, 2], 'D': [1, 2, 3], 'F': [1], 'g': None, # day + 'E': [1, 2, 3, 4, 5, 6], 'e': [1, 2, 3, 4, 5, 6], 'c': [1, 3, 4, 5, 6], # week day + 'a': [1, 2, 3, 4, 5], 'b': [1, 2, 3, 4, 5], 'B': [1, 2, 3, 4, 5], # period + 'h': [1, 2], 'H': [1, 2], 'K': [1, 2], 'k': [1, 2], # hour + 'm': [1, 2], # minute + 's': [1, 2], 'S': None, 'A': None, # second + 'z': [1, 2, 3, 4], 'Z': [1, 2, 3, 4, 5], 'O': [1, 4], 'v': [1, 4], # zone + 'V': [1, 2, 3, 4], 'x': [1, 2, 3, 4, 5], 'X': [1, 2, 3, 4, 5], # zone +} + +#: The pattern characters declared in the Date Field Symbol Table +#: (https://www.unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table) +#: in order of decreasing magnitude. +PATTERN_CHAR_ORDER = "GyYuUQqMLlwWdDFgEecabBChHKkjJmsSAzZOvVXx" + + +def parse_pattern(pattern: str | DateTimePattern) -> DateTimePattern: + """Parse date, time, and datetime format patterns. + + >>> parse_pattern("MMMMd").format + u'%(MMMM)s%(d)s' + >>> parse_pattern("MMM d, yyyy").format + u'%(MMM)s %(d)s, %(yyyy)s' + + Pattern can contain literal strings in single quotes: + + >>> parse_pattern("H:mm' Uhr 'z").format + u'%(H)s:%(mm)s Uhr %(z)s' + + An actual single quote can be used by using two adjacent single quote + characters: + + >>> parse_pattern("hh' o''clock'").format + u"%(hh)s o'clock" + + :param pattern: the formatting pattern to parse + """ + if isinstance(pattern, DateTimePattern): + return pattern + return _cached_parse_pattern(pattern) + + +@lru_cache(maxsize=1024) +def _cached_parse_pattern(pattern: str) -> DateTimePattern: + result = [] + + for tok_type, tok_value in tokenize_pattern(pattern): + if tok_type == "chars": + result.append(tok_value.replace('%', '%%')) + elif tok_type == "field": + fieldchar, fieldnum = tok_value + limit = PATTERN_CHARS[fieldchar] + if limit and fieldnum not in limit: + raise ValueError(f"Invalid length for field: {fieldchar * fieldnum!r}") + result.append('%%(%s)s' % (fieldchar * fieldnum)) + else: + raise NotImplementedError(f"Unknown token type: {tok_type}") + return DateTimePattern(pattern, ''.join(result)) + + +def tokenize_pattern(pattern: str) -> list[tuple[str, str | tuple[str, int]]]: + """ + Tokenize date format patterns. + + Returns a list of (token_type, token_value) tuples. + + ``token_type`` may be either "chars" or "field". + + For "chars" tokens, the value is the literal value. + + For "field" tokens, the value is a tuple of (field character, repetition count). + + :param pattern: Pattern string + :type pattern: str + :rtype: list[tuple] + """ + result = [] + quotebuf = None + charbuf = [] + fieldchar = [''] + fieldnum = [0] + + def append_chars(): + result.append(('chars', ''.join(charbuf).replace('\0', "'"))) + del charbuf[:] + + def append_field(): + result.append(('field', (fieldchar[0], fieldnum[0]))) + fieldchar[0] = '' + fieldnum[0] = 0 + + for char in pattern.replace("''", '\0'): + if quotebuf is None: + if char == "'": # quote started + if fieldchar[0]: + append_field() + elif charbuf: + append_chars() + quotebuf = [] + elif char in PATTERN_CHARS: + if charbuf: + append_chars() + if char == fieldchar[0]: + fieldnum[0] += 1 + else: + if fieldchar[0]: + append_field() + fieldchar[0] = char + fieldnum[0] = 1 + else: + if fieldchar[0]: + append_field() + charbuf.append(char) + + elif quotebuf is not None: + if char == "'": # end of quote + charbuf.extend(quotebuf) + quotebuf = None + else: # inside quote + quotebuf.append(char) + + if fieldchar[0]: + append_field() + elif charbuf: + append_chars() + + return result + + +def untokenize_pattern(tokens: Iterable[tuple[str, str | tuple[str, int]]]) -> str: + """ + Turn a date format pattern token stream back into a string. + + This is the reverse operation of ``tokenize_pattern``. + + :type tokens: Iterable[tuple] + :rtype: str + """ + output = [] + for tok_type, tok_value in tokens: + if tok_type == "field": + output.append(tok_value[0] * tok_value[1]) + elif tok_type == "chars": + if not any(ch in PATTERN_CHARS for ch in tok_value): # No need to quote + output.append(tok_value) + else: + output.append("'%s'" % tok_value.replace("'", "''")) + return "".join(output) + + +def split_interval_pattern(pattern: str) -> list[str]: + """ + Split an interval-describing datetime pattern into multiple pieces. + + > The pattern is then designed to be broken up into two pieces by determining the first repeating field. + - https://www.unicode.org/reports/tr35/tr35-dates.html#intervalFormats + + >>> split_interval_pattern(u'E d.M. \u2013 E d.M.') + [u'E d.M. \u2013 ', 'E d.M.'] + >>> split_interval_pattern("Y 'text' Y 'more text'") + ["Y 'text '", "Y 'more text'"] + >>> split_interval_pattern(u"E, MMM d \u2013 E") + [u'E, MMM d \u2013 ', u'E'] + >>> split_interval_pattern("MMM d") + ['MMM d'] + >>> split_interval_pattern("y G") + ['y G'] + >>> split_interval_pattern(u"MMM d \u2013 d") + [u'MMM d \u2013 ', u'd'] + + :param pattern: Interval pattern string + :return: list of "subpatterns" + """ + + seen_fields = set() + parts = [[]] + + for tok_type, tok_value in tokenize_pattern(pattern): + if tok_type == "field": + if tok_value[0] in seen_fields: # Repeated field + parts.append([]) + seen_fields.clear() + seen_fields.add(tok_value[0]) + parts[-1].append((tok_type, tok_value)) + + return [untokenize_pattern(tokens) for tokens in parts] + + +def match_skeleton(skeleton: str, options: Iterable[str], allow_different_fields: bool = False) -> str | None: + """ + Find the closest match for the given datetime skeleton among the options given. + + This uses the rules outlined in the TR35 document. + + >>> match_skeleton('yMMd', ('yMd', 'yMMMd')) + 'yMd' + + >>> match_skeleton('yMMd', ('jyMMd',), allow_different_fields=True) + 'jyMMd' + + >>> match_skeleton('yMMd', ('qyMMd',), allow_different_fields=False) + + >>> match_skeleton('hmz', ('hmv',)) + 'hmv' + + :param skeleton: The skeleton to match + :type skeleton: str + :param options: An iterable of other skeletons to match against + :type options: Iterable[str] + :param allow_different_fields: Whether to allow a match that uses different fields + than the skeleton requested. + :type allow_different_fields: bool + + :return: The closest skeleton match, or if no match was found, None. + :rtype: str|None + """ + + # TODO: maybe implement pattern expansion? + + # Based on the implementation in + # https://github.com/unicode-org/icu/blob/main/icu4j/main/core/src/main/java/com/ibm/icu/text/DateIntervalInfo.java + + # Filter out falsy values and sort for stability; when `interval_formats` is passed in, there may be a None key. + options = sorted(option for option in options if option) + + if 'z' in skeleton and not any('z' in option for option in options): + skeleton = skeleton.replace('z', 'v') + if 'k' in skeleton and not any('k' in option for option in options): + skeleton = skeleton.replace('k', 'H') + if 'K' in skeleton and not any('K' in option for option in options): + skeleton = skeleton.replace('K', 'h') + if 'a' in skeleton and not any('a' in option for option in options): + skeleton = skeleton.replace('a', '') + if 'b' in skeleton and not any('b' in option for option in options): + skeleton = skeleton.replace('b', '') + + get_input_field_width = dict(t[1] for t in tokenize_pattern(skeleton) if t[0] == "field").get + best_skeleton = None + best_distance = None + for option in options: + get_opt_field_width = dict(t[1] for t in tokenize_pattern(option) if t[0] == "field").get + distance = 0 + for field in PATTERN_CHARS: + input_width = get_input_field_width(field, 0) + opt_width = get_opt_field_width(field, 0) + if input_width == opt_width: + continue + if opt_width == 0 or input_width == 0: + if not allow_different_fields: # This one is not okay + option = None + break + distance += 0x1000 # Magic weight constant for "entirely different fields" + elif field == 'M' and ((input_width > 2 and opt_width <= 2) or (input_width <= 2 and opt_width > 2)): + distance += 0x100 # Magic weight for "text turns into a number" + else: + distance += abs(input_width - opt_width) + + if not option: # We lost the option along the way (probably due to "allow_different_fields") + continue + + if not best_skeleton or distance < best_distance: + best_skeleton = option + best_distance = distance + + if distance == 0: # Found a perfect match! + break + + return best_skeleton diff --git a/venv/lib/python3.12/site-packages/babel/global.dat b/venv/lib/python3.12/site-packages/babel/global.dat new file mode 100644 index 00000000..4c7f1506 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/global.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/languages.py b/venv/lib/python3.12/site-packages/babel/languages.py new file mode 100644 index 00000000..564f555d --- /dev/null +++ b/venv/lib/python3.12/site-packages/babel/languages.py @@ -0,0 +1,72 @@ +from __future__ import annotations + +from babel.core import get_global + + +def get_official_languages(territory: str, regional: bool = False, de_facto: bool = False) -> tuple[str, ...]: + """ + Get the official language(s) for the given territory. + + The language codes, if any are known, are returned in order of descending popularity. + + If the `regional` flag is set, then languages which are regionally official are also returned. + + If the `de_facto` flag is set, then languages which are "de facto" official are also returned. + + .. warning:: Note that the data is as up to date as the current version of the CLDR used + by Babel. If you need scientifically accurate information, use another source! + + :param territory: Territory code + :type territory: str + :param regional: Whether to return regionally official languages too + :type regional: bool + :param de_facto: Whether to return de-facto official languages too + :type de_facto: bool + :return: Tuple of language codes + :rtype: tuple[str] + """ + + territory = str(territory).upper() + allowed_stati = {"official"} + if regional: + allowed_stati.add("official_regional") + if de_facto: + allowed_stati.add("de_facto_official") + + languages = get_global("territory_languages").get(territory, {}) + pairs = [ + (info['population_percent'], language) + for language, info in languages.items() + if info.get('official_status') in allowed_stati + ] + pairs.sort(reverse=True) + return tuple(lang for _, lang in pairs) + + +def get_territory_language_info(territory: str) -> dict[str, dict[str, float | str | None]]: + """ + Get a dictionary of language information for a territory. + + The dictionary is keyed by language code; the values are dicts with more information. + + The following keys are currently known for the values: + + * `population_percent`: The percentage of the territory's population speaking the + language. + * `official_status`: An optional string describing the officiality status of the language. + Known values are "official", "official_regional" and "de_facto_official". + + .. warning:: Note that the data is as up to date as the current version of the CLDR used + by Babel. If you need scientifically accurate information, use another source! + + .. note:: Note that the format of the dict returned may change between Babel versions. + + See https://www.unicode.org/cldr/charts/latest/supplemental/territory_language_information.html + + :param territory: Territory code + :type territory: str + :return: Language information dictionary + :rtype: dict[str, dict] + """ + territory = str(territory).upper() + return get_global("territory_languages").get(territory, {}).copy() diff --git a/venv/lib/python3.12/site-packages/babel/lists.py b/venv/lib/python3.12/site-packages/babel/lists.py new file mode 100644 index 00000000..353171c7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/babel/lists.py @@ -0,0 +1,132 @@ +""" + babel.lists + ~~~~~~~~~~~ + + Locale dependent formatting of lists. + + The default locale for the functions in this module is determined by the + following environment variables, in that order: + + * ``LC_ALL``, and + * ``LANG`` + + :copyright: (c) 2015-2025 by the Babel Team. + :license: BSD, see LICENSE for more details. +""" +from __future__ import annotations + +import warnings +from collections.abc import Sequence +from typing import Literal + +from babel.core import Locale, default_locale + +_DEFAULT_LOCALE = default_locale() # TODO(3.0): Remove this. + + +def __getattr__(name): + if name == "DEFAULT_LOCALE": + warnings.warn( + "The babel.lists.DEFAULT_LOCALE constant is deprecated and will be removed.", + DeprecationWarning, + stacklevel=2, + ) + return _DEFAULT_LOCALE + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + + +def format_list( + lst: Sequence[str], + style: Literal['standard', 'standard-short', 'or', 'or-short', 'unit', 'unit-short', 'unit-narrow'] = 'standard', + locale: Locale | str | None = None, +) -> str: + """ + Format the items in `lst` as a list. + + >>> format_list(['apples', 'oranges', 'pears'], locale='en') + u'apples, oranges, and pears' + >>> format_list(['apples', 'oranges', 'pears'], locale='zh') + u'apples\u3001oranges\u548cpears' + >>> format_list(['omena', 'peruna', 'aplari'], style='or', locale='fi') + u'omena, peruna tai aplari' + + Not all styles are necessarily available in all locales. + The function will attempt to fall back to replacement styles according to the rules + set forth in the CLDR root XML file, and raise a ValueError if no suitable replacement + can be found. + + The following text is verbatim from the Unicode TR35-49 spec [1]. + + * standard: + A typical 'and' list for arbitrary placeholders. + eg. "January, February, and March" + * standard-short: + A short version of an 'and' list, suitable for use with short or abbreviated placeholder values. + eg. "Jan., Feb., and Mar." + * or: + A typical 'or' list for arbitrary placeholders. + eg. "January, February, or March" + * or-short: + A short version of an 'or' list. + eg. "Jan., Feb., or Mar." + * unit: + A list suitable for wide units. + eg. "3 feet, 7 inches" + * unit-short: + A list suitable for short units + eg. "3 ft, 7 in" + * unit-narrow: + A list suitable for narrow units, where space on the screen is very limited. + eg. "3′ 7″" + + [1]: https://www.unicode.org/reports/tr35/tr35-49/tr35-general.html#ListPatterns + + :param lst: a sequence of items to format in to a list + :param style: the style to format the list with. See above for description. + :param locale: the locale. Defaults to the system locale. + """ + locale = Locale.parse(locale or _DEFAULT_LOCALE) + if not lst: + return '' + if len(lst) == 1: + return lst[0] + + patterns = _resolve_list_style(locale, style) + + if len(lst) == 2 and '2' in patterns: + return patterns['2'].format(*lst) + + result = patterns['start'].format(lst[0], lst[1]) + for elem in lst[2:-1]: + result = patterns['middle'].format(result, elem) + result = patterns['end'].format(result, lst[-1]) + + return result + + +# Based on CLDR 45's root.xml file's ``es. +# The root file defines both `standard` and `or`, +# so they're always available. +# TODO: It would likely be better to use the +# babel.localedata.Alias mechanism for this, +# but I'm not quite sure how it's supposed to +# work with inheritance and data in the root. +_style_fallbacks = { + "or-narrow": ["or-short", "or"], + "or-short": ["or"], + "standard-narrow": ["standard-short", "standard"], + "standard-short": ["standard"], + "unit": ["unit-short", "standard"], + "unit-narrow": ["unit-short", "unit", "standard"], + "unit-short": ["standard"], +} + + +def _resolve_list_style(locale: Locale, style: str): + for style in (style, *(_style_fallbacks.get(style, []))): # noqa: B020 + if style in locale.list_patterns: + return locale.list_patterns[style] + raise ValueError( + f"Locale {locale} does not support list formatting style {style!r} " + f"(supported are {sorted(locale.list_patterns)})", + ) diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/LICENSE.unicode b/venv/lib/python3.12/site-packages/babel/locale-data/LICENSE.unicode new file mode 100644 index 00000000..861b74f3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/babel/locale-data/LICENSE.unicode @@ -0,0 +1,41 @@ +UNICODE LICENSE V3 + +COPYRIGHT AND PERMISSION NOTICE + +Copyright © 2004-2025 Unicode, Inc. + +NOTICE TO USER: Carefully read the following legal agreement. BY +DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR +SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE +TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT +DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of data files and any associated documentation (the "Data Files") or +software and any associated documentation (the "Software") to deal in the +Data Files or Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, and/or sell +copies of the Data Files or Software, and to permit persons to whom the +Data Files or Software are furnished to do so, provided that either (a) +this copyright and permission notice appear with all copies of the Data +Files or Software, or (b) this copyright and permission notice appear in +associated Documentation. + +THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF +THIRD PARTY RIGHTS. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE +BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, +OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA +FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or other +dealings in these Data Files or Software without prior written +authorization of the copyright holder. + +SPDX-License-Identifier: Unicode-3.0 diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/aa.dat b/venv/lib/python3.12/site-packages/babel/locale-data/aa.dat new file mode 100644 index 00000000..96b76a00 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/aa.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/aa_DJ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/aa_DJ.dat new file mode 100644 index 00000000..a3cb2563 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/aa_DJ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/aa_ER.dat b/venv/lib/python3.12/site-packages/babel/locale-data/aa_ER.dat new file mode 100644 index 00000000..a01edfca Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/aa_ER.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/aa_ET.dat b/venv/lib/python3.12/site-packages/babel/locale-data/aa_ET.dat new file mode 100644 index 00000000..119b891b Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/aa_ET.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ab.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ab.dat new file mode 100644 index 00000000..e1fbc6e6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ab.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ab_GE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ab_GE.dat new file mode 100644 index 00000000..15a980a7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ab_GE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/af.dat b/venv/lib/python3.12/site-packages/babel/locale-data/af.dat new file mode 100644 index 00000000..a1e347f6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/af.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/af_NA.dat b/venv/lib/python3.12/site-packages/babel/locale-data/af_NA.dat new file mode 100644 index 00000000..d5ff2a31 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/af_NA.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/af_ZA.dat b/venv/lib/python3.12/site-packages/babel/locale-data/af_ZA.dat new file mode 100644 index 00000000..34779331 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/af_ZA.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/agq.dat b/venv/lib/python3.12/site-packages/babel/locale-data/agq.dat new file mode 100644 index 00000000..a4eb919c Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/agq.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/agq_CM.dat b/venv/lib/python3.12/site-packages/babel/locale-data/agq_CM.dat new file mode 100644 index 00000000..57701c90 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/agq_CM.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ak.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ak.dat new file mode 100644 index 00000000..6c937699 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ak.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ak_GH.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ak_GH.dat new file mode 100644 index 00000000..a7c63dae Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ak_GH.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/am.dat b/venv/lib/python3.12/site-packages/babel/locale-data/am.dat new file mode 100644 index 00000000..3647c436 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/am.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/am_ET.dat b/venv/lib/python3.12/site-packages/babel/locale-data/am_ET.dat new file mode 100644 index 00000000..050cc76c Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/am_ET.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/an.dat b/venv/lib/python3.12/site-packages/babel/locale-data/an.dat new file mode 100644 index 00000000..1678ebce Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/an.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/an_ES.dat b/venv/lib/python3.12/site-packages/babel/locale-data/an_ES.dat new file mode 100644 index 00000000..a11156aa Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/an_ES.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ann.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ann.dat new file mode 100644 index 00000000..cee6e2bb Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ann.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ann_NG.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ann_NG.dat new file mode 100644 index 00000000..612c3f89 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ann_NG.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/apc.dat b/venv/lib/python3.12/site-packages/babel/locale-data/apc.dat new file mode 100644 index 00000000..644ec2a1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/apc.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/apc_SY.dat b/venv/lib/python3.12/site-packages/babel/locale-data/apc_SY.dat new file mode 100644 index 00000000..6c5e15a7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/apc_SY.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ar.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ar.dat new file mode 100644 index 00000000..30da0f40 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ar.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ar_001.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ar_001.dat new file mode 100644 index 00000000..bdc0475f Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ar_001.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ar_AE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ar_AE.dat new file mode 100644 index 00000000..f8386597 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ar_AE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ar_BH.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ar_BH.dat new file mode 100644 index 00000000..1792ea6b Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ar_BH.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ar_DJ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ar_DJ.dat new file mode 100644 index 00000000..579056a5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ar_DJ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ar_DZ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ar_DZ.dat new file mode 100644 index 00000000..2d61edcd Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ar_DZ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ar_EG.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ar_EG.dat new file mode 100644 index 00000000..d9be03e1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ar_EG.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ar_EH.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ar_EH.dat new file mode 100644 index 00000000..df44a58b Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ar_EH.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ar_ER.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ar_ER.dat new file mode 100644 index 00000000..cd036430 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ar_ER.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ar_IL.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ar_IL.dat new file mode 100644 index 00000000..557c47f4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ar_IL.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ar_IQ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ar_IQ.dat new file mode 100644 index 00000000..5b3d21c4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ar_IQ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ar_JO.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ar_JO.dat new file mode 100644 index 00000000..8bf13f24 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ar_JO.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ar_KM.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ar_KM.dat new file mode 100644 index 00000000..88f1ac48 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ar_KM.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ar_KW.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ar_KW.dat new file mode 100644 index 00000000..80762a47 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ar_KW.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ar_LB.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ar_LB.dat new file mode 100644 index 00000000..060dfa49 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ar_LB.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ar_LY.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ar_LY.dat new file mode 100644 index 00000000..a870e157 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ar_LY.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ar_MA.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ar_MA.dat new file mode 100644 index 00000000..ecdc22a5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ar_MA.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ar_MR.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ar_MR.dat new file mode 100644 index 00000000..7d66b5cf Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ar_MR.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ar_OM.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ar_OM.dat new file mode 100644 index 00000000..d318d99f Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ar_OM.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ar_PS.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ar_PS.dat new file mode 100644 index 00000000..2c3ef839 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ar_PS.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ar_QA.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ar_QA.dat new file mode 100644 index 00000000..d187cc66 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ar_QA.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ar_SA.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ar_SA.dat new file mode 100644 index 00000000..ae1ecaed Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ar_SA.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ar_SD.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ar_SD.dat new file mode 100644 index 00000000..0ce23d8a Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ar_SD.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ar_SO.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ar_SO.dat new file mode 100644 index 00000000..ed6648bc Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ar_SO.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ar_SS.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ar_SS.dat new file mode 100644 index 00000000..a2cadef7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ar_SS.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ar_SY.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ar_SY.dat new file mode 100644 index 00000000..701de7ad Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ar_SY.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ar_TD.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ar_TD.dat new file mode 100644 index 00000000..deb0d9c4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ar_TD.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ar_TN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ar_TN.dat new file mode 100644 index 00000000..46853cec Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ar_TN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ar_YE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ar_YE.dat new file mode 100644 index 00000000..8c2212b5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ar_YE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/arn.dat b/venv/lib/python3.12/site-packages/babel/locale-data/arn.dat new file mode 100644 index 00000000..5abcc770 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/arn.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/arn_CL.dat b/venv/lib/python3.12/site-packages/babel/locale-data/arn_CL.dat new file mode 100644 index 00000000..4aadebb1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/arn_CL.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/as.dat b/venv/lib/python3.12/site-packages/babel/locale-data/as.dat new file mode 100644 index 00000000..4f1f6cc4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/as.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/as_IN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/as_IN.dat new file mode 100644 index 00000000..9f24d230 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/as_IN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/asa.dat b/venv/lib/python3.12/site-packages/babel/locale-data/asa.dat new file mode 100644 index 00000000..6c4f84f4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/asa.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/asa_TZ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/asa_TZ.dat new file mode 100644 index 00000000..0b2adf25 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/asa_TZ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ast.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ast.dat new file mode 100644 index 00000000..11a786a0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ast.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ast_ES.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ast_ES.dat new file mode 100644 index 00000000..af96de06 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ast_ES.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/az.dat b/venv/lib/python3.12/site-packages/babel/locale-data/az.dat new file mode 100644 index 00000000..5ee74802 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/az.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/az_Arab.dat b/venv/lib/python3.12/site-packages/babel/locale-data/az_Arab.dat new file mode 100644 index 00000000..d18ccb51 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/az_Arab.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/az_Arab_IQ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/az_Arab_IQ.dat new file mode 100644 index 00000000..393175fc Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/az_Arab_IQ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/az_Arab_IR.dat b/venv/lib/python3.12/site-packages/babel/locale-data/az_Arab_IR.dat new file mode 100644 index 00000000..8df9df25 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/az_Arab_IR.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/az_Arab_TR.dat b/venv/lib/python3.12/site-packages/babel/locale-data/az_Arab_TR.dat new file mode 100644 index 00000000..99fa84c4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/az_Arab_TR.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/az_Cyrl.dat b/venv/lib/python3.12/site-packages/babel/locale-data/az_Cyrl.dat new file mode 100644 index 00000000..2f79fbd0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/az_Cyrl.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/az_Cyrl_AZ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/az_Cyrl_AZ.dat new file mode 100644 index 00000000..75ee5220 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/az_Cyrl_AZ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/az_Latn.dat b/venv/lib/python3.12/site-packages/babel/locale-data/az_Latn.dat new file mode 100644 index 00000000..88c306fe Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/az_Latn.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/az_Latn_AZ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/az_Latn_AZ.dat new file mode 100644 index 00000000..75ee5220 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/az_Latn_AZ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ba.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ba.dat new file mode 100644 index 00000000..99bc134f Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ba.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ba_RU.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ba_RU.dat new file mode 100644 index 00000000..93dadc0f Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ba_RU.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/bal.dat b/venv/lib/python3.12/site-packages/babel/locale-data/bal.dat new file mode 100644 index 00000000..6135e5a3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/bal.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/bal_Arab.dat b/venv/lib/python3.12/site-packages/babel/locale-data/bal_Arab.dat new file mode 100644 index 00000000..9fc6e82e Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/bal_Arab.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/bal_Arab_PK.dat b/venv/lib/python3.12/site-packages/babel/locale-data/bal_Arab_PK.dat new file mode 100644 index 00000000..37f77e20 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/bal_Arab_PK.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/bal_Latn.dat b/venv/lib/python3.12/site-packages/babel/locale-data/bal_Latn.dat new file mode 100644 index 00000000..8a7c8c09 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/bal_Latn.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/bal_Latn_PK.dat b/venv/lib/python3.12/site-packages/babel/locale-data/bal_Latn_PK.dat new file mode 100644 index 00000000..37f77e20 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/bal_Latn_PK.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/bas.dat b/venv/lib/python3.12/site-packages/babel/locale-data/bas.dat new file mode 100644 index 00000000..6afbc89d Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/bas.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/bas_CM.dat b/venv/lib/python3.12/site-packages/babel/locale-data/bas_CM.dat new file mode 100644 index 00000000..992e8632 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/bas_CM.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/be.dat b/venv/lib/python3.12/site-packages/babel/locale-data/be.dat new file mode 100644 index 00000000..160a752f Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/be.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/be_BY.dat b/venv/lib/python3.12/site-packages/babel/locale-data/be_BY.dat new file mode 100644 index 00000000..759f67a1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/be_BY.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/be_TARASK.dat b/venv/lib/python3.12/site-packages/babel/locale-data/be_TARASK.dat new file mode 100644 index 00000000..7e895362 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/be_TARASK.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/bem.dat b/venv/lib/python3.12/site-packages/babel/locale-data/bem.dat new file mode 100644 index 00000000..35c138fc Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/bem.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/bem_ZM.dat b/venv/lib/python3.12/site-packages/babel/locale-data/bem_ZM.dat new file mode 100644 index 00000000..7f393a8b Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/bem_ZM.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/bew.dat b/venv/lib/python3.12/site-packages/babel/locale-data/bew.dat new file mode 100644 index 00000000..798011d5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/bew.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/bew_ID.dat b/venv/lib/python3.12/site-packages/babel/locale-data/bew_ID.dat new file mode 100644 index 00000000..12cfbff6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/bew_ID.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/bez.dat b/venv/lib/python3.12/site-packages/babel/locale-data/bez.dat new file mode 100644 index 00000000..04e40d18 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/bez.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/bez_TZ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/bez_TZ.dat new file mode 100644 index 00000000..76974eb1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/bez_TZ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/bg.dat b/venv/lib/python3.12/site-packages/babel/locale-data/bg.dat new file mode 100644 index 00000000..dfd5660a Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/bg.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/bg_BG.dat b/venv/lib/python3.12/site-packages/babel/locale-data/bg_BG.dat new file mode 100644 index 00000000..b69fe979 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/bg_BG.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/bgc.dat b/venv/lib/python3.12/site-packages/babel/locale-data/bgc.dat new file mode 100644 index 00000000..1a3cdd55 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/bgc.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/bgc_IN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/bgc_IN.dat new file mode 100644 index 00000000..bc2e0217 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/bgc_IN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/bgn.dat b/venv/lib/python3.12/site-packages/babel/locale-data/bgn.dat new file mode 100644 index 00000000..f8297eb3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/bgn.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/bgn_AE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/bgn_AE.dat new file mode 100644 index 00000000..0b17e78f Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/bgn_AE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/bgn_AF.dat b/venv/lib/python3.12/site-packages/babel/locale-data/bgn_AF.dat new file mode 100644 index 00000000..4062c74d Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/bgn_AF.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/bgn_IR.dat b/venv/lib/python3.12/site-packages/babel/locale-data/bgn_IR.dat new file mode 100644 index 00000000..99a1b500 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/bgn_IR.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/bgn_OM.dat b/venv/lib/python3.12/site-packages/babel/locale-data/bgn_OM.dat new file mode 100644 index 00000000..f3962683 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/bgn_OM.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/bgn_PK.dat b/venv/lib/python3.12/site-packages/babel/locale-data/bgn_PK.dat new file mode 100644 index 00000000..e15afea2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/bgn_PK.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/bho.dat b/venv/lib/python3.12/site-packages/babel/locale-data/bho.dat new file mode 100644 index 00000000..15f164ee Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/bho.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/bho_IN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/bho_IN.dat new file mode 100644 index 00000000..4f7eb58e Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/bho_IN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/blo.dat b/venv/lib/python3.12/site-packages/babel/locale-data/blo.dat new file mode 100644 index 00000000..6952c5e3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/blo.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/blo_BJ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/blo_BJ.dat new file mode 100644 index 00000000..fbc0ae51 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/blo_BJ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/blt.dat b/venv/lib/python3.12/site-packages/babel/locale-data/blt.dat new file mode 100644 index 00000000..fee95211 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/blt.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/blt_VN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/blt_VN.dat new file mode 100644 index 00000000..1cf8aa30 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/blt_VN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/bm.dat b/venv/lib/python3.12/site-packages/babel/locale-data/bm.dat new file mode 100644 index 00000000..35a8748f Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/bm.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/bm_ML.dat b/venv/lib/python3.12/site-packages/babel/locale-data/bm_ML.dat new file mode 100644 index 00000000..db406084 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/bm_ML.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/bm_Nkoo.dat b/venv/lib/python3.12/site-packages/babel/locale-data/bm_Nkoo.dat new file mode 100644 index 00000000..318955fe Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/bm_Nkoo.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/bm_Nkoo_ML.dat b/venv/lib/python3.12/site-packages/babel/locale-data/bm_Nkoo_ML.dat new file mode 100644 index 00000000..db406084 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/bm_Nkoo_ML.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/bn.dat b/venv/lib/python3.12/site-packages/babel/locale-data/bn.dat new file mode 100644 index 00000000..d6d5dadf Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/bn.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/bn_BD.dat b/venv/lib/python3.12/site-packages/babel/locale-data/bn_BD.dat new file mode 100644 index 00000000..0814ec91 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/bn_BD.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/bn_IN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/bn_IN.dat new file mode 100644 index 00000000..ea4c51c3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/bn_IN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/bo.dat b/venv/lib/python3.12/site-packages/babel/locale-data/bo.dat new file mode 100644 index 00000000..3e914649 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/bo.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/bo_CN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/bo_CN.dat new file mode 100644 index 00000000..d9e62431 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/bo_CN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/bo_IN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/bo_IN.dat new file mode 100644 index 00000000..900a19e7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/bo_IN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/br.dat b/venv/lib/python3.12/site-packages/babel/locale-data/br.dat new file mode 100644 index 00000000..6ee99829 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/br.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/br_FR.dat b/venv/lib/python3.12/site-packages/babel/locale-data/br_FR.dat new file mode 100644 index 00000000..5964d661 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/br_FR.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/brx.dat b/venv/lib/python3.12/site-packages/babel/locale-data/brx.dat new file mode 100644 index 00000000..db3f702a Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/brx.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/brx_IN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/brx_IN.dat new file mode 100644 index 00000000..6dafcc92 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/brx_IN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/bs.dat b/venv/lib/python3.12/site-packages/babel/locale-data/bs.dat new file mode 100644 index 00000000..9e9a5e5e Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/bs.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/bs_Cyrl.dat b/venv/lib/python3.12/site-packages/babel/locale-data/bs_Cyrl.dat new file mode 100644 index 00000000..2047a88b Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/bs_Cyrl.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/bs_Cyrl_BA.dat b/venv/lib/python3.12/site-packages/babel/locale-data/bs_Cyrl_BA.dat new file mode 100644 index 00000000..4a56da8f Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/bs_Cyrl_BA.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/bs_Latn.dat b/venv/lib/python3.12/site-packages/babel/locale-data/bs_Latn.dat new file mode 100644 index 00000000..767af2e9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/bs_Latn.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/bs_Latn_BA.dat b/venv/lib/python3.12/site-packages/babel/locale-data/bs_Latn_BA.dat new file mode 100644 index 00000000..4a56da8f Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/bs_Latn_BA.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/bss.dat b/venv/lib/python3.12/site-packages/babel/locale-data/bss.dat new file mode 100644 index 00000000..9b59977c Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/bss.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/bss_CM.dat b/venv/lib/python3.12/site-packages/babel/locale-data/bss_CM.dat new file mode 100644 index 00000000..c02e93f2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/bss_CM.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/byn.dat b/venv/lib/python3.12/site-packages/babel/locale-data/byn.dat new file mode 100644 index 00000000..c8a14266 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/byn.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/byn_ER.dat b/venv/lib/python3.12/site-packages/babel/locale-data/byn_ER.dat new file mode 100644 index 00000000..ef7364c0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/byn_ER.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ca.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ca.dat new file mode 100644 index 00000000..89fa5d88 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ca.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ca_AD.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ca_AD.dat new file mode 100644 index 00000000..417de470 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ca_AD.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ca_ES.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ca_ES.dat new file mode 100644 index 00000000..1262e7c7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ca_ES.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ca_ES_VALENCIA.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ca_ES_VALENCIA.dat new file mode 100644 index 00000000..f9a67d74 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ca_ES_VALENCIA.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ca_FR.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ca_FR.dat new file mode 100644 index 00000000..1e13497d Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ca_FR.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ca_IT.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ca_IT.dat new file mode 100644 index 00000000..18daef70 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ca_IT.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/cad.dat b/venv/lib/python3.12/site-packages/babel/locale-data/cad.dat new file mode 100644 index 00000000..e0751fea Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/cad.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/cad_US.dat b/venv/lib/python3.12/site-packages/babel/locale-data/cad_US.dat new file mode 100644 index 00000000..649a1277 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/cad_US.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/cch.dat b/venv/lib/python3.12/site-packages/babel/locale-data/cch.dat new file mode 100644 index 00000000..6bfb265e Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/cch.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/cch_NG.dat b/venv/lib/python3.12/site-packages/babel/locale-data/cch_NG.dat new file mode 100644 index 00000000..7ca50d06 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/cch_NG.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ccp.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ccp.dat new file mode 100644 index 00000000..3c5578b2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ccp.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ccp_BD.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ccp_BD.dat new file mode 100644 index 00000000..23248d87 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ccp_BD.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ccp_IN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ccp_IN.dat new file mode 100644 index 00000000..e4fe9a01 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ccp_IN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ce.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ce.dat new file mode 100644 index 00000000..a37c7047 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ce.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ce_RU.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ce_RU.dat new file mode 100644 index 00000000..613de660 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ce_RU.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ceb.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ceb.dat new file mode 100644 index 00000000..d6b8f188 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ceb.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ceb_PH.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ceb_PH.dat new file mode 100644 index 00000000..f5a88a13 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ceb_PH.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/cgg.dat b/venv/lib/python3.12/site-packages/babel/locale-data/cgg.dat new file mode 100644 index 00000000..5687c38b Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/cgg.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/cgg_UG.dat b/venv/lib/python3.12/site-packages/babel/locale-data/cgg_UG.dat new file mode 100644 index 00000000..3ed7de5e Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/cgg_UG.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/cho.dat b/venv/lib/python3.12/site-packages/babel/locale-data/cho.dat new file mode 100644 index 00000000..8d978b7c Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/cho.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/cho_US.dat b/venv/lib/python3.12/site-packages/babel/locale-data/cho_US.dat new file mode 100644 index 00000000..927cd451 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/cho_US.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/chr.dat b/venv/lib/python3.12/site-packages/babel/locale-data/chr.dat new file mode 100644 index 00000000..557280d4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/chr.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/chr_US.dat b/venv/lib/python3.12/site-packages/babel/locale-data/chr_US.dat new file mode 100644 index 00000000..30579852 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/chr_US.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/cic.dat b/venv/lib/python3.12/site-packages/babel/locale-data/cic.dat new file mode 100644 index 00000000..87877563 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/cic.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/cic_US.dat b/venv/lib/python3.12/site-packages/babel/locale-data/cic_US.dat new file mode 100644 index 00000000..a2f944fb Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/cic_US.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ckb.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ckb.dat new file mode 100644 index 00000000..e8f1be3e Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ckb.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ckb_IQ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ckb_IQ.dat new file mode 100644 index 00000000..68ae78b5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ckb_IQ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ckb_IR.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ckb_IR.dat new file mode 100644 index 00000000..407a6058 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ckb_IR.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/co.dat b/venv/lib/python3.12/site-packages/babel/locale-data/co.dat new file mode 100644 index 00000000..cf9e7bc6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/co.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/co_FR.dat b/venv/lib/python3.12/site-packages/babel/locale-data/co_FR.dat new file mode 100644 index 00000000..ac968523 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/co_FR.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/cs.dat b/venv/lib/python3.12/site-packages/babel/locale-data/cs.dat new file mode 100644 index 00000000..a00782bc Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/cs.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/cs_CZ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/cs_CZ.dat new file mode 100644 index 00000000..0c492c42 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/cs_CZ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/csw.dat b/venv/lib/python3.12/site-packages/babel/locale-data/csw.dat new file mode 100644 index 00000000..1224a99e Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/csw.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/csw_CA.dat b/venv/lib/python3.12/site-packages/babel/locale-data/csw_CA.dat new file mode 100644 index 00000000..01b6c607 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/csw_CA.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/cu.dat b/venv/lib/python3.12/site-packages/babel/locale-data/cu.dat new file mode 100644 index 00000000..19443c52 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/cu.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/cu_RU.dat b/venv/lib/python3.12/site-packages/babel/locale-data/cu_RU.dat new file mode 100644 index 00000000..a9d3101e Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/cu_RU.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/cv.dat b/venv/lib/python3.12/site-packages/babel/locale-data/cv.dat new file mode 100644 index 00000000..6e21fed3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/cv.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/cv_RU.dat b/venv/lib/python3.12/site-packages/babel/locale-data/cv_RU.dat new file mode 100644 index 00000000..59759914 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/cv_RU.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/cy.dat b/venv/lib/python3.12/site-packages/babel/locale-data/cy.dat new file mode 100644 index 00000000..d0d1e3c4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/cy.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/cy_GB.dat b/venv/lib/python3.12/site-packages/babel/locale-data/cy_GB.dat new file mode 100644 index 00000000..f3febac6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/cy_GB.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/da.dat b/venv/lib/python3.12/site-packages/babel/locale-data/da.dat new file mode 100644 index 00000000..bb74382c Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/da.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/da_DK.dat b/venv/lib/python3.12/site-packages/babel/locale-data/da_DK.dat new file mode 100644 index 00000000..dbd06720 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/da_DK.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/da_GL.dat b/venv/lib/python3.12/site-packages/babel/locale-data/da_GL.dat new file mode 100644 index 00000000..ce46da12 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/da_GL.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/dav.dat b/venv/lib/python3.12/site-packages/babel/locale-data/dav.dat new file mode 100644 index 00000000..8b63c2b6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/dav.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/dav_KE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/dav_KE.dat new file mode 100644 index 00000000..5e3fbf48 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/dav_KE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/de.dat b/venv/lib/python3.12/site-packages/babel/locale-data/de.dat new file mode 100644 index 00000000..7cf17cc2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/de.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/de_AT.dat b/venv/lib/python3.12/site-packages/babel/locale-data/de_AT.dat new file mode 100644 index 00000000..c8b9b62e Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/de_AT.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/de_BE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/de_BE.dat new file mode 100644 index 00000000..806b2914 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/de_BE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/de_CH.dat b/venv/lib/python3.12/site-packages/babel/locale-data/de_CH.dat new file mode 100644 index 00000000..9f50b2e1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/de_CH.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/de_DE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/de_DE.dat new file mode 100644 index 00000000..1d769ac8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/de_DE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/de_IT.dat b/venv/lib/python3.12/site-packages/babel/locale-data/de_IT.dat new file mode 100644 index 00000000..7d7b9d2d Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/de_IT.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/de_LI.dat b/venv/lib/python3.12/site-packages/babel/locale-data/de_LI.dat new file mode 100644 index 00000000..f4f1a353 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/de_LI.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/de_LU.dat b/venv/lib/python3.12/site-packages/babel/locale-data/de_LU.dat new file mode 100644 index 00000000..ed9d8fb7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/de_LU.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/dje.dat b/venv/lib/python3.12/site-packages/babel/locale-data/dje.dat new file mode 100644 index 00000000..aafbdbda Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/dje.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/dje_NE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/dje_NE.dat new file mode 100644 index 00000000..605bb83a Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/dje_NE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/doi.dat b/venv/lib/python3.12/site-packages/babel/locale-data/doi.dat new file mode 100644 index 00000000..44b79c89 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/doi.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/doi_IN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/doi_IN.dat new file mode 100644 index 00000000..550ec0ec Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/doi_IN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/dsb.dat b/venv/lib/python3.12/site-packages/babel/locale-data/dsb.dat new file mode 100644 index 00000000..4a954bde Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/dsb.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/dsb_DE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/dsb_DE.dat new file mode 100644 index 00000000..f1a8d4bc Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/dsb_DE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/dua.dat b/venv/lib/python3.12/site-packages/babel/locale-data/dua.dat new file mode 100644 index 00000000..24ee4953 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/dua.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/dua_CM.dat b/venv/lib/python3.12/site-packages/babel/locale-data/dua_CM.dat new file mode 100644 index 00000000..5eab8c9b Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/dua_CM.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/dv.dat b/venv/lib/python3.12/site-packages/babel/locale-data/dv.dat new file mode 100644 index 00000000..f8c88ff8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/dv.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/dv_MV.dat b/venv/lib/python3.12/site-packages/babel/locale-data/dv_MV.dat new file mode 100644 index 00000000..6a6048b5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/dv_MV.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/dyo.dat b/venv/lib/python3.12/site-packages/babel/locale-data/dyo.dat new file mode 100644 index 00000000..836d954d Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/dyo.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/dyo_SN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/dyo_SN.dat new file mode 100644 index 00000000..734192ce Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/dyo_SN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/dz.dat b/venv/lib/python3.12/site-packages/babel/locale-data/dz.dat new file mode 100644 index 00000000..a75ae5bd Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/dz.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/dz_BT.dat b/venv/lib/python3.12/site-packages/babel/locale-data/dz_BT.dat new file mode 100644 index 00000000..34b2803b Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/dz_BT.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ebu.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ebu.dat new file mode 100644 index 00000000..d02c22a0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ebu.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ebu_KE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ebu_KE.dat new file mode 100644 index 00000000..0abfe0e4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ebu_KE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ee.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ee.dat new file mode 100644 index 00000000..9348aa2e Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ee.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ee_GH.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ee_GH.dat new file mode 100644 index 00000000..3702d427 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ee_GH.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ee_TG.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ee_TG.dat new file mode 100644 index 00000000..5f19ac9f Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ee_TG.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/el.dat b/venv/lib/python3.12/site-packages/babel/locale-data/el.dat new file mode 100644 index 00000000..dee09ba5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/el.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/el_CY.dat b/venv/lib/python3.12/site-packages/babel/locale-data/el_CY.dat new file mode 100644 index 00000000..22fb8748 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/el_CY.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/el_GR.dat b/venv/lib/python3.12/site-packages/babel/locale-data/el_GR.dat new file mode 100644 index 00000000..ee095c95 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/el_GR.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/el_POLYTON.dat b/venv/lib/python3.12/site-packages/babel/locale-data/el_POLYTON.dat new file mode 100644 index 00000000..24a37da5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/el_POLYTON.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en.dat new file mode 100644 index 00000000..7a576fce Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_001.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_001.dat new file mode 100644 index 00000000..47db2cc6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_001.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_150.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_150.dat new file mode 100644 index 00000000..82dd2718 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_150.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_AE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_AE.dat new file mode 100644 index 00000000..bd96c499 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_AE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_AG.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_AG.dat new file mode 100644 index 00000000..16d7fa5a Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_AG.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_AI.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_AI.dat new file mode 100644 index 00000000..6f0437d6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_AI.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_AS.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_AS.dat new file mode 100644 index 00000000..d309d365 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_AS.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_AT.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_AT.dat new file mode 100644 index 00000000..ff190b61 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_AT.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_AU.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_AU.dat new file mode 100644 index 00000000..7c92a1a3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_AU.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_BB.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_BB.dat new file mode 100644 index 00000000..fbcfdd8a Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_BB.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_BE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_BE.dat new file mode 100644 index 00000000..481689fe Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_BE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_BI.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_BI.dat new file mode 100644 index 00000000..e2ef90f8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_BI.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_BM.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_BM.dat new file mode 100644 index 00000000..f4e57b6a Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_BM.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_BS.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_BS.dat new file mode 100644 index 00000000..aafa96f0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_BS.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_BW.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_BW.dat new file mode 100644 index 00000000..2d083923 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_BW.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_BZ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_BZ.dat new file mode 100644 index 00000000..26326dcb Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_BZ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_CA.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_CA.dat new file mode 100644 index 00000000..89cab7ce Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_CA.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_CC.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_CC.dat new file mode 100644 index 00000000..1102d2b5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_CC.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_CH.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_CH.dat new file mode 100644 index 00000000..bce7db95 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_CH.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_CK.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_CK.dat new file mode 100644 index 00000000..e4870c84 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_CK.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_CM.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_CM.dat new file mode 100644 index 00000000..1816a83f Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_CM.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_CX.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_CX.dat new file mode 100644 index 00000000..b0a5620b Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_CX.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_CY.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_CY.dat new file mode 100644 index 00000000..54fb2d1a Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_CY.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_DE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_DE.dat new file mode 100644 index 00000000..3d7ded02 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_DE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_DG.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_DG.dat new file mode 100644 index 00000000..1bdea430 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_DG.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_DK.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_DK.dat new file mode 100644 index 00000000..cae041ed Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_DK.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_DM.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_DM.dat new file mode 100644 index 00000000..00ce5e14 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_DM.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_Dsrt.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_Dsrt.dat new file mode 100644 index 00000000..114accc1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_Dsrt.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_Dsrt_US.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_Dsrt_US.dat new file mode 100644 index 00000000..663e8261 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_Dsrt_US.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_ER.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_ER.dat new file mode 100644 index 00000000..349173e3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_ER.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_FI.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_FI.dat new file mode 100644 index 00000000..90a35e72 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_FI.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_FJ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_FJ.dat new file mode 100644 index 00000000..e2013194 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_FJ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_FK.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_FK.dat new file mode 100644 index 00000000..3b586dd8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_FK.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_FM.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_FM.dat new file mode 100644 index 00000000..4a4d6ab9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_FM.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_GB.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_GB.dat new file mode 100644 index 00000000..ce4687f5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_GB.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_GD.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_GD.dat new file mode 100644 index 00000000..fbd024b4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_GD.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_GG.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_GG.dat new file mode 100644 index 00000000..6195e7d8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_GG.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_GH.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_GH.dat new file mode 100644 index 00000000..bfcd6f57 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_GH.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_GI.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_GI.dat new file mode 100644 index 00000000..98b852ea Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_GI.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_GM.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_GM.dat new file mode 100644 index 00000000..4e7daad8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_GM.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_GU.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_GU.dat new file mode 100644 index 00000000..6d184675 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_GU.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_GY.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_GY.dat new file mode 100644 index 00000000..4192d41b Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_GY.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_HK.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_HK.dat new file mode 100644 index 00000000..39a6d7f6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_HK.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_ID.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_ID.dat new file mode 100644 index 00000000..763ffdf5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_ID.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_IE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_IE.dat new file mode 100644 index 00000000..36b4ffb1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_IE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_IL.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_IL.dat new file mode 100644 index 00000000..9782e31a Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_IL.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_IM.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_IM.dat new file mode 100644 index 00000000..a3789694 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_IM.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_IN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_IN.dat new file mode 100644 index 00000000..03987cb1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_IN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_IO.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_IO.dat new file mode 100644 index 00000000..ce4dba27 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_IO.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_JE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_JE.dat new file mode 100644 index 00000000..59fad126 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_JE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_JM.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_JM.dat new file mode 100644 index 00000000..8bb03e32 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_JM.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_KE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_KE.dat new file mode 100644 index 00000000..5c181679 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_KE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_KI.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_KI.dat new file mode 100644 index 00000000..2c57a160 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_KI.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_KN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_KN.dat new file mode 100644 index 00000000..79c9a818 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_KN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_KY.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_KY.dat new file mode 100644 index 00000000..013312f6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_KY.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_LC.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_LC.dat new file mode 100644 index 00000000..0368fe36 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_LC.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_LR.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_LR.dat new file mode 100644 index 00000000..80d77d8c Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_LR.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_LS.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_LS.dat new file mode 100644 index 00000000..b3cbbfb6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_LS.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_MG.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_MG.dat new file mode 100644 index 00000000..cf3db95a Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_MG.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_MH.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_MH.dat new file mode 100644 index 00000000..f6bc501c Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_MH.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_MO.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_MO.dat new file mode 100644 index 00000000..6635dc53 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_MO.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_MP.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_MP.dat new file mode 100644 index 00000000..ede9c2bb Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_MP.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_MS.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_MS.dat new file mode 100644 index 00000000..53115eb5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_MS.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_MT.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_MT.dat new file mode 100644 index 00000000..87c2b953 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_MT.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_MU.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_MU.dat new file mode 100644 index 00000000..524b3ac9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_MU.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_MV.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_MV.dat new file mode 100644 index 00000000..852a33d9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_MV.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_MW.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_MW.dat new file mode 100644 index 00000000..d0dcd6a9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_MW.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_MY.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_MY.dat new file mode 100644 index 00000000..9479385f Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_MY.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_NA.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_NA.dat new file mode 100644 index 00000000..a3d88293 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_NA.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_NF.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_NF.dat new file mode 100644 index 00000000..8003f609 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_NF.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_NG.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_NG.dat new file mode 100644 index 00000000..27bae075 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_NG.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_NL.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_NL.dat new file mode 100644 index 00000000..8cd57735 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_NL.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_NR.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_NR.dat new file mode 100644 index 00000000..0c788a8d Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_NR.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_NU.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_NU.dat new file mode 100644 index 00000000..f8607f64 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_NU.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_NZ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_NZ.dat new file mode 100644 index 00000000..7220c4cc Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_NZ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_PG.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_PG.dat new file mode 100644 index 00000000..4269e331 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_PG.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_PH.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_PH.dat new file mode 100644 index 00000000..9ce4fc0b Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_PH.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_PK.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_PK.dat new file mode 100644 index 00000000..f198d9ec Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_PK.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_PN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_PN.dat new file mode 100644 index 00000000..9977c743 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_PN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_PR.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_PR.dat new file mode 100644 index 00000000..cdb83dbe Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_PR.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_PW.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_PW.dat new file mode 100644 index 00000000..e878083f Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_PW.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_RW.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_RW.dat new file mode 100644 index 00000000..c46733b0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_RW.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_SB.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_SB.dat new file mode 100644 index 00000000..b81625e3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_SB.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_SC.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_SC.dat new file mode 100644 index 00000000..4816bb86 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_SC.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_SD.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_SD.dat new file mode 100644 index 00000000..985e3e6b Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_SD.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_SE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_SE.dat new file mode 100644 index 00000000..5f89bad5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_SE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_SG.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_SG.dat new file mode 100644 index 00000000..a21a72a7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_SG.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_SH.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_SH.dat new file mode 100644 index 00000000..006487c3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_SH.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_SI.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_SI.dat new file mode 100644 index 00000000..97b42d04 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_SI.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_SL.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_SL.dat new file mode 100644 index 00000000..127d1285 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_SL.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_SS.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_SS.dat new file mode 100644 index 00000000..7192fd13 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_SS.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_SX.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_SX.dat new file mode 100644 index 00000000..5dfff7d8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_SX.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_SZ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_SZ.dat new file mode 100644 index 00000000..90a9f7a1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_SZ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_Shaw.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_Shaw.dat new file mode 100644 index 00000000..6c394365 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_Shaw.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_Shaw_GB.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_Shaw_GB.dat new file mode 100644 index 00000000..214e5c61 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_Shaw_GB.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_TC.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_TC.dat new file mode 100644 index 00000000..d1edd041 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_TC.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_TK.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_TK.dat new file mode 100644 index 00000000..d81803f7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_TK.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_TO.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_TO.dat new file mode 100644 index 00000000..4512ee44 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_TO.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_TT.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_TT.dat new file mode 100644 index 00000000..e704f065 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_TT.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_TV.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_TV.dat new file mode 100644 index 00000000..c936c695 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_TV.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_TZ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_TZ.dat new file mode 100644 index 00000000..62091cee Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_TZ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_UG.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_UG.dat new file mode 100644 index 00000000..dd077cca Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_UG.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_UM.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_UM.dat new file mode 100644 index 00000000..3a027b2a Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_UM.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_US.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_US.dat new file mode 100644 index 00000000..663e8261 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_US.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_US_POSIX.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_US_POSIX.dat new file mode 100644 index 00000000..a9ecf5b4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_US_POSIX.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_VC.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_VC.dat new file mode 100644 index 00000000..86fb17d9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_VC.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_VG.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_VG.dat new file mode 100644 index 00000000..3fdd46cb Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_VG.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_VI.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_VI.dat new file mode 100644 index 00000000..c4520cc6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_VI.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_VU.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_VU.dat new file mode 100644 index 00000000..12647a8c Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_VU.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_WS.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_WS.dat new file mode 100644 index 00000000..8531d311 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_WS.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_ZA.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_ZA.dat new file mode 100644 index 00000000..8cc6b0e0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_ZA.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_ZM.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_ZM.dat new file mode 100644 index 00000000..50039250 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_ZM.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/en_ZW.dat b/venv/lib/python3.12/site-packages/babel/locale-data/en_ZW.dat new file mode 100644 index 00000000..6a8835f2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/en_ZW.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/eo.dat b/venv/lib/python3.12/site-packages/babel/locale-data/eo.dat new file mode 100644 index 00000000..84ba5ece Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/eo.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/eo_001.dat b/venv/lib/python3.12/site-packages/babel/locale-data/eo_001.dat new file mode 100644 index 00000000..ec48fb1f Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/eo_001.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/es.dat b/venv/lib/python3.12/site-packages/babel/locale-data/es.dat new file mode 100644 index 00000000..0a7e6ed7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/es.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/es_419.dat b/venv/lib/python3.12/site-packages/babel/locale-data/es_419.dat new file mode 100644 index 00000000..38c8aa5b Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/es_419.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/es_AR.dat b/venv/lib/python3.12/site-packages/babel/locale-data/es_AR.dat new file mode 100644 index 00000000..42787bdf Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/es_AR.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/es_BO.dat b/venv/lib/python3.12/site-packages/babel/locale-data/es_BO.dat new file mode 100644 index 00000000..df6a1299 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/es_BO.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/es_BR.dat b/venv/lib/python3.12/site-packages/babel/locale-data/es_BR.dat new file mode 100644 index 00000000..7d1f1023 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/es_BR.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/es_BZ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/es_BZ.dat new file mode 100644 index 00000000..ff0ff6ab Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/es_BZ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/es_CL.dat b/venv/lib/python3.12/site-packages/babel/locale-data/es_CL.dat new file mode 100644 index 00000000..87cd6243 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/es_CL.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/es_CO.dat b/venv/lib/python3.12/site-packages/babel/locale-data/es_CO.dat new file mode 100644 index 00000000..09e03e3a Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/es_CO.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/es_CR.dat b/venv/lib/python3.12/site-packages/babel/locale-data/es_CR.dat new file mode 100644 index 00000000..041296d1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/es_CR.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/es_CU.dat b/venv/lib/python3.12/site-packages/babel/locale-data/es_CU.dat new file mode 100644 index 00000000..ced5b381 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/es_CU.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/es_DO.dat b/venv/lib/python3.12/site-packages/babel/locale-data/es_DO.dat new file mode 100644 index 00000000..cbec7812 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/es_DO.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/es_EA.dat b/venv/lib/python3.12/site-packages/babel/locale-data/es_EA.dat new file mode 100644 index 00000000..dfd0d910 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/es_EA.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/es_EC.dat b/venv/lib/python3.12/site-packages/babel/locale-data/es_EC.dat new file mode 100644 index 00000000..0c6f37b6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/es_EC.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/es_ES.dat b/venv/lib/python3.12/site-packages/babel/locale-data/es_ES.dat new file mode 100644 index 00000000..3414dff2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/es_ES.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/es_GQ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/es_GQ.dat new file mode 100644 index 00000000..775e43b0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/es_GQ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/es_GT.dat b/venv/lib/python3.12/site-packages/babel/locale-data/es_GT.dat new file mode 100644 index 00000000..bedab31c Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/es_GT.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/es_HN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/es_HN.dat new file mode 100644 index 00000000..c5223aae Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/es_HN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/es_IC.dat b/venv/lib/python3.12/site-packages/babel/locale-data/es_IC.dat new file mode 100644 index 00000000..3a9f9fd9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/es_IC.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/es_MX.dat b/venv/lib/python3.12/site-packages/babel/locale-data/es_MX.dat new file mode 100644 index 00000000..91919c15 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/es_MX.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/es_NI.dat b/venv/lib/python3.12/site-packages/babel/locale-data/es_NI.dat new file mode 100644 index 00000000..9e1ab78f Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/es_NI.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/es_PA.dat b/venv/lib/python3.12/site-packages/babel/locale-data/es_PA.dat new file mode 100644 index 00000000..edfcd545 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/es_PA.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/es_PE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/es_PE.dat new file mode 100644 index 00000000..82c50ad2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/es_PE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/es_PH.dat b/venv/lib/python3.12/site-packages/babel/locale-data/es_PH.dat new file mode 100644 index 00000000..91e24f13 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/es_PH.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/es_PR.dat b/venv/lib/python3.12/site-packages/babel/locale-data/es_PR.dat new file mode 100644 index 00000000..c45d5911 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/es_PR.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/es_PY.dat b/venv/lib/python3.12/site-packages/babel/locale-data/es_PY.dat new file mode 100644 index 00000000..3f365705 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/es_PY.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/es_SV.dat b/venv/lib/python3.12/site-packages/babel/locale-data/es_SV.dat new file mode 100644 index 00000000..2c98032c Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/es_SV.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/es_US.dat b/venv/lib/python3.12/site-packages/babel/locale-data/es_US.dat new file mode 100644 index 00000000..03388117 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/es_US.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/es_UY.dat b/venv/lib/python3.12/site-packages/babel/locale-data/es_UY.dat new file mode 100644 index 00000000..292fa0cc Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/es_UY.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/es_VE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/es_VE.dat new file mode 100644 index 00000000..42cba455 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/es_VE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/et.dat b/venv/lib/python3.12/site-packages/babel/locale-data/et.dat new file mode 100644 index 00000000..c242fb8e Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/et.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/et_EE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/et_EE.dat new file mode 100644 index 00000000..a08c6824 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/et_EE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/eu.dat b/venv/lib/python3.12/site-packages/babel/locale-data/eu.dat new file mode 100644 index 00000000..e01ff5be Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/eu.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/eu_ES.dat b/venv/lib/python3.12/site-packages/babel/locale-data/eu_ES.dat new file mode 100644 index 00000000..62f7cc61 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/eu_ES.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ewo.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ewo.dat new file mode 100644 index 00000000..5f7d570b Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ewo.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ewo_CM.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ewo_CM.dat new file mode 100644 index 00000000..ae6b0f7a Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ewo_CM.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fa.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fa.dat new file mode 100644 index 00000000..5a0663af Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fa.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fa_AF.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fa_AF.dat new file mode 100644 index 00000000..689b4c78 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fa_AF.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fa_IR.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fa_IR.dat new file mode 100644 index 00000000..b3dd8eb5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fa_IR.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ff.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ff.dat new file mode 100644 index 00000000..531fffa9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ff.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm.dat new file mode 100644 index 00000000..5c7dc646 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_BF.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_BF.dat new file mode 100644 index 00000000..84f0ffbd Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_BF.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_CM.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_CM.dat new file mode 100644 index 00000000..78bf6208 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_CM.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_GH.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_GH.dat new file mode 100644 index 00000000..6667263b Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_GH.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_GM.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_GM.dat new file mode 100644 index 00000000..e641b279 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_GM.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_GN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_GN.dat new file mode 100644 index 00000000..216df0ba Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_GN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_GW.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_GW.dat new file mode 100644 index 00000000..63799caa Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_GW.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_LR.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_LR.dat new file mode 100644 index 00000000..f5e3cdeb Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_LR.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_MR.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_MR.dat new file mode 100644 index 00000000..716f23fd Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_MR.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_NE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_NE.dat new file mode 100644 index 00000000..12098834 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_NE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_NG.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_NG.dat new file mode 100644 index 00000000..a17d85c5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_NG.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_SL.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_SL.dat new file mode 100644 index 00000000..d8825521 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_SL.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_SN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_SN.dat new file mode 100644 index 00000000..125d9994 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_SN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn.dat new file mode 100644 index 00000000..df806dd4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_BF.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_BF.dat new file mode 100644 index 00000000..bb3a2e0e Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_BF.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_CM.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_CM.dat new file mode 100644 index 00000000..a3229cdc Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_CM.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_GH.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_GH.dat new file mode 100644 index 00000000..58222300 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_GH.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_GM.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_GM.dat new file mode 100644 index 00000000..82022dfa Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_GM.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_GN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_GN.dat new file mode 100644 index 00000000..1a99d48f Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_GN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_GW.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_GW.dat new file mode 100644 index 00000000..a2b55bf7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_GW.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_LR.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_LR.dat new file mode 100644 index 00000000..5dce447e Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_LR.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_MR.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_MR.dat new file mode 100644 index 00000000..e6178b86 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_MR.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_NE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_NE.dat new file mode 100644 index 00000000..82151e8f Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_NE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_NG.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_NG.dat new file mode 100644 index 00000000..086889e6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_NG.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_SL.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_SL.dat new file mode 100644 index 00000000..e50753de Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_SL.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_SN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_SN.dat new file mode 100644 index 00000000..faeff87c Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_SN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fi.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fi.dat new file mode 100644 index 00000000..310cd1c4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fi.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fi_FI.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fi_FI.dat new file mode 100644 index 00000000..fa20daec Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fi_FI.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fil.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fil.dat new file mode 100644 index 00000000..03bcac01 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fil.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fil_PH.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fil_PH.dat new file mode 100644 index 00000000..dc08338c Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fil_PH.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fo.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fo.dat new file mode 100644 index 00000000..7a0b53f6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fo.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fo_DK.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fo_DK.dat new file mode 100644 index 00000000..669088d7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fo_DK.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fo_FO.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fo_FO.dat new file mode 100644 index 00000000..7d8a8145 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fo_FO.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fr.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fr.dat new file mode 100644 index 00000000..fb8c3a1d Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fr.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fr_BE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fr_BE.dat new file mode 100644 index 00000000..cc842fe0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fr_BE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fr_BF.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fr_BF.dat new file mode 100644 index 00000000..ba1ecc57 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fr_BF.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fr_BI.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fr_BI.dat new file mode 100644 index 00000000..d4036bed Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fr_BI.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fr_BJ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fr_BJ.dat new file mode 100644 index 00000000..562758e8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fr_BJ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fr_BL.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fr_BL.dat new file mode 100644 index 00000000..e311ee66 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fr_BL.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fr_CA.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fr_CA.dat new file mode 100644 index 00000000..da870066 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fr_CA.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fr_CD.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fr_CD.dat new file mode 100644 index 00000000..24827887 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fr_CD.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fr_CF.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fr_CF.dat new file mode 100644 index 00000000..2468e1ed Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fr_CF.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fr_CG.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fr_CG.dat new file mode 100644 index 00000000..b7df1a92 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fr_CG.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fr_CH.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fr_CH.dat new file mode 100644 index 00000000..fa5b7b47 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fr_CH.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fr_CI.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fr_CI.dat new file mode 100644 index 00000000..3d92afcf Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fr_CI.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fr_CM.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fr_CM.dat new file mode 100644 index 00000000..a779b4a7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fr_CM.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fr_DJ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fr_DJ.dat new file mode 100644 index 00000000..79aa6e67 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fr_DJ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fr_DZ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fr_DZ.dat new file mode 100644 index 00000000..3f56d231 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fr_DZ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fr_FR.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fr_FR.dat new file mode 100644 index 00000000..553d5e4a Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fr_FR.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fr_GA.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fr_GA.dat new file mode 100644 index 00000000..beb0dd56 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fr_GA.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fr_GF.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fr_GF.dat new file mode 100644 index 00000000..eb9ffc90 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fr_GF.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fr_GN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fr_GN.dat new file mode 100644 index 00000000..c4f9f808 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fr_GN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fr_GP.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fr_GP.dat new file mode 100644 index 00000000..e2834c7d Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fr_GP.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fr_GQ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fr_GQ.dat new file mode 100644 index 00000000..a04de173 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fr_GQ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fr_HT.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fr_HT.dat new file mode 100644 index 00000000..33b39563 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fr_HT.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fr_KM.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fr_KM.dat new file mode 100644 index 00000000..9ece5936 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fr_KM.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fr_LU.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fr_LU.dat new file mode 100644 index 00000000..062b9ab0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fr_LU.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fr_MA.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fr_MA.dat new file mode 100644 index 00000000..6ee8eb9f Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fr_MA.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fr_MC.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fr_MC.dat new file mode 100644 index 00000000..52315a3d Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fr_MC.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fr_MF.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fr_MF.dat new file mode 100644 index 00000000..e5a1df0e Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fr_MF.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fr_MG.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fr_MG.dat new file mode 100644 index 00000000..c01d050a Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fr_MG.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fr_ML.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fr_ML.dat new file mode 100644 index 00000000..721771ea Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fr_ML.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fr_MQ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fr_MQ.dat new file mode 100644 index 00000000..90becb76 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fr_MQ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fr_MR.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fr_MR.dat new file mode 100644 index 00000000..28ef3f14 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fr_MR.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fr_MU.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fr_MU.dat new file mode 100644 index 00000000..76fc2ab4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fr_MU.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fr_NC.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fr_NC.dat new file mode 100644 index 00000000..6aaf0342 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fr_NC.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fr_NE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fr_NE.dat new file mode 100644 index 00000000..143ac4fd Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fr_NE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fr_PF.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fr_PF.dat new file mode 100644 index 00000000..e2f11dcd Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fr_PF.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fr_PM.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fr_PM.dat new file mode 100644 index 00000000..4f9cdf7b Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fr_PM.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fr_RE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fr_RE.dat new file mode 100644 index 00000000..214ad4fa Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fr_RE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fr_RW.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fr_RW.dat new file mode 100644 index 00000000..6739c522 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fr_RW.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fr_SC.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fr_SC.dat new file mode 100644 index 00000000..1fedefe5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fr_SC.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fr_SN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fr_SN.dat new file mode 100644 index 00000000..48207856 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fr_SN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fr_SY.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fr_SY.dat new file mode 100644 index 00000000..8dd53967 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fr_SY.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fr_TD.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fr_TD.dat new file mode 100644 index 00000000..6abebabd Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fr_TD.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fr_TG.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fr_TG.dat new file mode 100644 index 00000000..9395eaaf Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fr_TG.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fr_TN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fr_TN.dat new file mode 100644 index 00000000..bf1e5515 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fr_TN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fr_VU.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fr_VU.dat new file mode 100644 index 00000000..f29eeed9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fr_VU.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fr_WF.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fr_WF.dat new file mode 100644 index 00000000..2d38ac6f Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fr_WF.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fr_YT.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fr_YT.dat new file mode 100644 index 00000000..9e9ba28c Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fr_YT.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/frr.dat b/venv/lib/python3.12/site-packages/babel/locale-data/frr.dat new file mode 100644 index 00000000..a6b2c748 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/frr.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/frr_DE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/frr_DE.dat new file mode 100644 index 00000000..ecab3304 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/frr_DE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fur.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fur.dat new file mode 100644 index 00000000..81a28ed2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fur.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fur_IT.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fur_IT.dat new file mode 100644 index 00000000..2c40e5c0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fur_IT.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fy.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fy.dat new file mode 100644 index 00000000..1036ee09 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fy.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/fy_NL.dat b/venv/lib/python3.12/site-packages/babel/locale-data/fy_NL.dat new file mode 100644 index 00000000..1a8f5811 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/fy_NL.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ga.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ga.dat new file mode 100644 index 00000000..087b6029 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ga.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ga_GB.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ga_GB.dat new file mode 100644 index 00000000..7432317e Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ga_GB.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ga_IE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ga_IE.dat new file mode 100644 index 00000000..422b05a5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ga_IE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/gaa.dat b/venv/lib/python3.12/site-packages/babel/locale-data/gaa.dat new file mode 100644 index 00000000..5028b29c Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/gaa.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/gaa_GH.dat b/venv/lib/python3.12/site-packages/babel/locale-data/gaa_GH.dat new file mode 100644 index 00000000..7312993b Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/gaa_GH.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/gd.dat b/venv/lib/python3.12/site-packages/babel/locale-data/gd.dat new file mode 100644 index 00000000..785f8fbf Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/gd.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/gd_GB.dat b/venv/lib/python3.12/site-packages/babel/locale-data/gd_GB.dat new file mode 100644 index 00000000..e3705785 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/gd_GB.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/gez.dat b/venv/lib/python3.12/site-packages/babel/locale-data/gez.dat new file mode 100644 index 00000000..c3fe514f Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/gez.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/gez_ER.dat b/venv/lib/python3.12/site-packages/babel/locale-data/gez_ER.dat new file mode 100644 index 00000000..20c18a52 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/gez_ER.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/gez_ET.dat b/venv/lib/python3.12/site-packages/babel/locale-data/gez_ET.dat new file mode 100644 index 00000000..a70d43dd Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/gez_ET.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/gl.dat b/venv/lib/python3.12/site-packages/babel/locale-data/gl.dat new file mode 100644 index 00000000..48f3577d Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/gl.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/gl_ES.dat b/venv/lib/python3.12/site-packages/babel/locale-data/gl_ES.dat new file mode 100644 index 00000000..d432bb25 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/gl_ES.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/gn.dat b/venv/lib/python3.12/site-packages/babel/locale-data/gn.dat new file mode 100644 index 00000000..2d6eb9f5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/gn.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/gn_PY.dat b/venv/lib/python3.12/site-packages/babel/locale-data/gn_PY.dat new file mode 100644 index 00000000..a217cc7a Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/gn_PY.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/gsw.dat b/venv/lib/python3.12/site-packages/babel/locale-data/gsw.dat new file mode 100644 index 00000000..0d241c69 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/gsw.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/gsw_CH.dat b/venv/lib/python3.12/site-packages/babel/locale-data/gsw_CH.dat new file mode 100644 index 00000000..39ed5a8c Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/gsw_CH.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/gsw_FR.dat b/venv/lib/python3.12/site-packages/babel/locale-data/gsw_FR.dat new file mode 100644 index 00000000..8687c5c1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/gsw_FR.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/gsw_LI.dat b/venv/lib/python3.12/site-packages/babel/locale-data/gsw_LI.dat new file mode 100644 index 00000000..2e72e060 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/gsw_LI.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/gu.dat b/venv/lib/python3.12/site-packages/babel/locale-data/gu.dat new file mode 100644 index 00000000..ceefbb29 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/gu.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/gu_IN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/gu_IN.dat new file mode 100644 index 00000000..a7071f70 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/gu_IN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/guz.dat b/venv/lib/python3.12/site-packages/babel/locale-data/guz.dat new file mode 100644 index 00000000..386e37bc Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/guz.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/guz_KE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/guz_KE.dat new file mode 100644 index 00000000..3b2c102d Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/guz_KE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/gv.dat b/venv/lib/python3.12/site-packages/babel/locale-data/gv.dat new file mode 100644 index 00000000..b493494c Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/gv.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/gv_IM.dat b/venv/lib/python3.12/site-packages/babel/locale-data/gv_IM.dat new file mode 100644 index 00000000..0793d838 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/gv_IM.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ha.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ha.dat new file mode 100644 index 00000000..74e86b98 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ha.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ha_Arab.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ha_Arab.dat new file mode 100644 index 00000000..1395d9e0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ha_Arab.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ha_Arab_NG.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ha_Arab_NG.dat new file mode 100644 index 00000000..0a98ac88 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ha_Arab_NG.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ha_Arab_SD.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ha_Arab_SD.dat new file mode 100644 index 00000000..e33c2b3c Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ha_Arab_SD.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ha_GH.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ha_GH.dat new file mode 100644 index 00000000..74d57805 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ha_GH.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ha_NE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ha_NE.dat new file mode 100644 index 00000000..d06afa95 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ha_NE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ha_NG.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ha_NG.dat new file mode 100644 index 00000000..0a98ac88 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ha_NG.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/haw.dat b/venv/lib/python3.12/site-packages/babel/locale-data/haw.dat new file mode 100644 index 00000000..8b2b47cd Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/haw.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/haw_US.dat b/venv/lib/python3.12/site-packages/babel/locale-data/haw_US.dat new file mode 100644 index 00000000..18706f16 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/haw_US.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/he.dat b/venv/lib/python3.12/site-packages/babel/locale-data/he.dat new file mode 100644 index 00000000..defc3ea7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/he.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/he_IL.dat b/venv/lib/python3.12/site-packages/babel/locale-data/he_IL.dat new file mode 100644 index 00000000..3a0da6c0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/he_IL.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/hi.dat b/venv/lib/python3.12/site-packages/babel/locale-data/hi.dat new file mode 100644 index 00000000..b2096453 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/hi.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/hi_IN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/hi_IN.dat new file mode 100644 index 00000000..9fbfd57d Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/hi_IN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/hi_Latn.dat b/venv/lib/python3.12/site-packages/babel/locale-data/hi_Latn.dat new file mode 100644 index 00000000..17b23a7d Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/hi_Latn.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/hi_Latn_IN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/hi_Latn_IN.dat new file mode 100644 index 00000000..9fbfd57d Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/hi_Latn_IN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/hnj.dat b/venv/lib/python3.12/site-packages/babel/locale-data/hnj.dat new file mode 100644 index 00000000..07eeb516 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/hnj.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/hnj_Hmnp.dat b/venv/lib/python3.12/site-packages/babel/locale-data/hnj_Hmnp.dat new file mode 100644 index 00000000..c8935a2e Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/hnj_Hmnp.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/hnj_Hmnp_US.dat b/venv/lib/python3.12/site-packages/babel/locale-data/hnj_Hmnp_US.dat new file mode 100644 index 00000000..46c37147 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/hnj_Hmnp_US.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/hr.dat b/venv/lib/python3.12/site-packages/babel/locale-data/hr.dat new file mode 100644 index 00000000..e2a005e8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/hr.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/hr_BA.dat b/venv/lib/python3.12/site-packages/babel/locale-data/hr_BA.dat new file mode 100644 index 00000000..44129abf Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/hr_BA.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/hr_HR.dat b/venv/lib/python3.12/site-packages/babel/locale-data/hr_HR.dat new file mode 100644 index 00000000..a0ae43dd Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/hr_HR.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/hsb.dat b/venv/lib/python3.12/site-packages/babel/locale-data/hsb.dat new file mode 100644 index 00000000..40b68464 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/hsb.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/hsb_DE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/hsb_DE.dat new file mode 100644 index 00000000..5de4a2c3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/hsb_DE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/hu.dat b/venv/lib/python3.12/site-packages/babel/locale-data/hu.dat new file mode 100644 index 00000000..143d1f0a Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/hu.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/hu_HU.dat b/venv/lib/python3.12/site-packages/babel/locale-data/hu_HU.dat new file mode 100644 index 00000000..113faede Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/hu_HU.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/hy.dat b/venv/lib/python3.12/site-packages/babel/locale-data/hy.dat new file mode 100644 index 00000000..a331f211 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/hy.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/hy_AM.dat b/venv/lib/python3.12/site-packages/babel/locale-data/hy_AM.dat new file mode 100644 index 00000000..fa2eaf7d Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/hy_AM.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ia.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ia.dat new file mode 100644 index 00000000..c9dab730 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ia.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ia_001.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ia_001.dat new file mode 100644 index 00000000..b0a1770b Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ia_001.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/id.dat b/venv/lib/python3.12/site-packages/babel/locale-data/id.dat new file mode 100644 index 00000000..d9f0472f Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/id.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/id_ID.dat b/venv/lib/python3.12/site-packages/babel/locale-data/id_ID.dat new file mode 100644 index 00000000..cf7c8e5e Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/id_ID.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ie.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ie.dat new file mode 100644 index 00000000..a6ae1fe3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ie.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ie_EE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ie_EE.dat new file mode 100644 index 00000000..91566139 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ie_EE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ig.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ig.dat new file mode 100644 index 00000000..4f4627c0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ig.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ig_NG.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ig_NG.dat new file mode 100644 index 00000000..4b5635b8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ig_NG.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ii.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ii.dat new file mode 100644 index 00000000..93158e63 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ii.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ii_CN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ii_CN.dat new file mode 100644 index 00000000..ef6effaa Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ii_CN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/io.dat b/venv/lib/python3.12/site-packages/babel/locale-data/io.dat new file mode 100644 index 00000000..33aa1fae Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/io.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/io_001.dat b/venv/lib/python3.12/site-packages/babel/locale-data/io_001.dat new file mode 100644 index 00000000..964a6ab6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/io_001.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/is.dat b/venv/lib/python3.12/site-packages/babel/locale-data/is.dat new file mode 100644 index 00000000..b0b107d7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/is.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/is_IS.dat b/venv/lib/python3.12/site-packages/babel/locale-data/is_IS.dat new file mode 100644 index 00000000..b3649a76 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/is_IS.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/it.dat b/venv/lib/python3.12/site-packages/babel/locale-data/it.dat new file mode 100644 index 00000000..f1e55e7f Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/it.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/it_CH.dat b/venv/lib/python3.12/site-packages/babel/locale-data/it_CH.dat new file mode 100644 index 00000000..cddfb6b5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/it_CH.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/it_IT.dat b/venv/lib/python3.12/site-packages/babel/locale-data/it_IT.dat new file mode 100644 index 00000000..b9a944aa Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/it_IT.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/it_SM.dat b/venv/lib/python3.12/site-packages/babel/locale-data/it_SM.dat new file mode 100644 index 00000000..db8432aa Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/it_SM.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/it_VA.dat b/venv/lib/python3.12/site-packages/babel/locale-data/it_VA.dat new file mode 100644 index 00000000..30ecca00 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/it_VA.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/iu.dat b/venv/lib/python3.12/site-packages/babel/locale-data/iu.dat new file mode 100644 index 00000000..ca5b1f7a Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/iu.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/iu_CA.dat b/venv/lib/python3.12/site-packages/babel/locale-data/iu_CA.dat new file mode 100644 index 00000000..61bb5448 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/iu_CA.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/iu_Latn.dat b/venv/lib/python3.12/site-packages/babel/locale-data/iu_Latn.dat new file mode 100644 index 00000000..20b7cedc Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/iu_Latn.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/iu_Latn_CA.dat b/venv/lib/python3.12/site-packages/babel/locale-data/iu_Latn_CA.dat new file mode 100644 index 00000000..61bb5448 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/iu_Latn_CA.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ja.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ja.dat new file mode 100644 index 00000000..769686f4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ja.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ja_JP.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ja_JP.dat new file mode 100644 index 00000000..f0d4479b Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ja_JP.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/jbo.dat b/venv/lib/python3.12/site-packages/babel/locale-data/jbo.dat new file mode 100644 index 00000000..1bf2dbb9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/jbo.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/jbo_001.dat b/venv/lib/python3.12/site-packages/babel/locale-data/jbo_001.dat new file mode 100644 index 00000000..dcc98aef Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/jbo_001.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/jgo.dat b/venv/lib/python3.12/site-packages/babel/locale-data/jgo.dat new file mode 100644 index 00000000..1b6230ad Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/jgo.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/jgo_CM.dat b/venv/lib/python3.12/site-packages/babel/locale-data/jgo_CM.dat new file mode 100644 index 00000000..7a523f3b Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/jgo_CM.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/jmc.dat b/venv/lib/python3.12/site-packages/babel/locale-data/jmc.dat new file mode 100644 index 00000000..a4abf969 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/jmc.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/jmc_TZ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/jmc_TZ.dat new file mode 100644 index 00000000..6abba327 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/jmc_TZ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/jv.dat b/venv/lib/python3.12/site-packages/babel/locale-data/jv.dat new file mode 100644 index 00000000..db18e761 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/jv.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/jv_ID.dat b/venv/lib/python3.12/site-packages/babel/locale-data/jv_ID.dat new file mode 100644 index 00000000..b60dc4db Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/jv_ID.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ka.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ka.dat new file mode 100644 index 00000000..c6dba51a Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ka.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ka_GE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ka_GE.dat new file mode 100644 index 00000000..73278fab Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ka_GE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kaa.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kaa.dat new file mode 100644 index 00000000..e6b6abdb Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kaa.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kaa_Cyrl.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kaa_Cyrl.dat new file mode 100644 index 00000000..085710f6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kaa_Cyrl.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kaa_Cyrl_UZ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kaa_Cyrl_UZ.dat new file mode 100644 index 00000000..b2e4590f Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kaa_Cyrl_UZ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kaa_Latn.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kaa_Latn.dat new file mode 100644 index 00000000..085710f6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kaa_Latn.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kaa_Latn_UZ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kaa_Latn_UZ.dat new file mode 100644 index 00000000..b2e4590f Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kaa_Latn_UZ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kab.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kab.dat new file mode 100644 index 00000000..b95fcc72 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kab.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kab_DZ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kab_DZ.dat new file mode 100644 index 00000000..c0cbad8b Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kab_DZ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kaj.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kaj.dat new file mode 100644 index 00000000..8d304e93 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kaj.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kaj_NG.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kaj_NG.dat new file mode 100644 index 00000000..a94d6f7c Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kaj_NG.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kam.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kam.dat new file mode 100644 index 00000000..2ef195ab Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kam.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kam_KE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kam_KE.dat new file mode 100644 index 00000000..d16ca2a1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kam_KE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kcg.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kcg.dat new file mode 100644 index 00000000..3ff14c35 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kcg.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kcg_NG.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kcg_NG.dat new file mode 100644 index 00000000..1e6dc36b Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kcg_NG.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kde.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kde.dat new file mode 100644 index 00000000..6d51d911 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kde.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kde_TZ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kde_TZ.dat new file mode 100644 index 00000000..2fcf1d47 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kde_TZ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kea.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kea.dat new file mode 100644 index 00000000..42d05295 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kea.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kea_CV.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kea_CV.dat new file mode 100644 index 00000000..da97962e Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kea_CV.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ken.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ken.dat new file mode 100644 index 00000000..57e91a15 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ken.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ken_CM.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ken_CM.dat new file mode 100644 index 00000000..9ef61800 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ken_CM.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kgp.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kgp.dat new file mode 100644 index 00000000..cfe272f7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kgp.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kgp_BR.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kgp_BR.dat new file mode 100644 index 00000000..44b499db Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kgp_BR.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/khq.dat b/venv/lib/python3.12/site-packages/babel/locale-data/khq.dat new file mode 100644 index 00000000..61cc45f5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/khq.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/khq_ML.dat b/venv/lib/python3.12/site-packages/babel/locale-data/khq_ML.dat new file mode 100644 index 00000000..91f7da18 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/khq_ML.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ki.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ki.dat new file mode 100644 index 00000000..0a7eb8d1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ki.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ki_KE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ki_KE.dat new file mode 100644 index 00000000..901b9214 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ki_KE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kk.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kk.dat new file mode 100644 index 00000000..19133c62 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kk.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kk_Arab.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kk_Arab.dat new file mode 100644 index 00000000..8dce5fc9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kk_Arab.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kk_Arab_CN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kk_Arab_CN.dat new file mode 100644 index 00000000..d8b9432a Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kk_Arab_CN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kk_Cyrl.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kk_Cyrl.dat new file mode 100644 index 00000000..082c7545 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kk_Cyrl.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kk_Cyrl_KZ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kk_Cyrl_KZ.dat new file mode 100644 index 00000000..ca827ac1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kk_Cyrl_KZ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kk_KZ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kk_KZ.dat new file mode 100644 index 00000000..ca827ac1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kk_KZ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kkj.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kkj.dat new file mode 100644 index 00000000..529c4175 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kkj.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kkj_CM.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kkj_CM.dat new file mode 100644 index 00000000..231f08bd Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kkj_CM.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kl.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kl.dat new file mode 100644 index 00000000..d82a25bb Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kl.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kl_GL.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kl_GL.dat new file mode 100644 index 00000000..a8fb28dc Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kl_GL.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kln.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kln.dat new file mode 100644 index 00000000..65e435cb Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kln.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kln_KE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kln_KE.dat new file mode 100644 index 00000000..9a12beb4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kln_KE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/km.dat b/venv/lib/python3.12/site-packages/babel/locale-data/km.dat new file mode 100644 index 00000000..52d3fc4f Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/km.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/km_KH.dat b/venv/lib/python3.12/site-packages/babel/locale-data/km_KH.dat new file mode 100644 index 00000000..2ccaba66 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/km_KH.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kn.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kn.dat new file mode 100644 index 00000000..85aaaec0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kn.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kn_IN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kn_IN.dat new file mode 100644 index 00000000..1f0d6462 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kn_IN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ko.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ko.dat new file mode 100644 index 00000000..765f2fcb Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ko.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ko_CN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ko_CN.dat new file mode 100644 index 00000000..170698df Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ko_CN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ko_KP.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ko_KP.dat new file mode 100644 index 00000000..7c76309a Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ko_KP.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ko_KR.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ko_KR.dat new file mode 100644 index 00000000..4af3b628 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ko_KR.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kok.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kok.dat new file mode 100644 index 00000000..f01857f2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kok.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kok_Deva.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kok_Deva.dat new file mode 100644 index 00000000..e92bf9d5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kok_Deva.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kok_Deva_IN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kok_Deva_IN.dat new file mode 100644 index 00000000..8fff8b19 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kok_Deva_IN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kok_Latn.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kok_Latn.dat new file mode 100644 index 00000000..d3fbcce6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kok_Latn.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kok_Latn_IN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kok_Latn_IN.dat new file mode 100644 index 00000000..8fff8b19 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kok_Latn_IN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kpe.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kpe.dat new file mode 100644 index 00000000..48cf0bf5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kpe.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kpe_GN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kpe_GN.dat new file mode 100644 index 00000000..d333167e Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kpe_GN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kpe_LR.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kpe_LR.dat new file mode 100644 index 00000000..8afe47b9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kpe_LR.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ks.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ks.dat new file mode 100644 index 00000000..96146c2c Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ks.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ks_Arab.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ks_Arab.dat new file mode 100644 index 00000000..6441efed Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ks_Arab.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ks_Arab_IN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ks_Arab_IN.dat new file mode 100644 index 00000000..02a5d116 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ks_Arab_IN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ks_Deva.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ks_Deva.dat new file mode 100644 index 00000000..e6380926 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ks_Deva.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ks_Deva_IN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ks_Deva_IN.dat new file mode 100644 index 00000000..02a5d116 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ks_Deva_IN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ksb.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ksb.dat new file mode 100644 index 00000000..7ced57e8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ksb.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ksb_TZ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ksb_TZ.dat new file mode 100644 index 00000000..6837e375 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ksb_TZ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ksf.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ksf.dat new file mode 100644 index 00000000..7fba2526 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ksf.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ksf_CM.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ksf_CM.dat new file mode 100644 index 00000000..fc29c498 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ksf_CM.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ksh.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ksh.dat new file mode 100644 index 00000000..b7bb1f17 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ksh.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ksh_DE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ksh_DE.dat new file mode 100644 index 00000000..9318d48d Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ksh_DE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ku.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ku.dat new file mode 100644 index 00000000..1bc6fa68 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ku.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ku_TR.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ku_TR.dat new file mode 100644 index 00000000..206426aa Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ku_TR.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kw.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kw.dat new file mode 100644 index 00000000..3d103e37 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kw.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kw_GB.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kw_GB.dat new file mode 100644 index 00000000..da81c10f Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kw_GB.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kxv.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kxv.dat new file mode 100644 index 00000000..2849d6cc Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kxv.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kxv_Deva.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kxv_Deva.dat new file mode 100644 index 00000000..184addcd Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kxv_Deva.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kxv_Deva_IN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kxv_Deva_IN.dat new file mode 100644 index 00000000..8a1e2898 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kxv_Deva_IN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kxv_Latn.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kxv_Latn.dat new file mode 100644 index 00000000..35b2695d Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kxv_Latn.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kxv_Latn_IN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kxv_Latn_IN.dat new file mode 100644 index 00000000..8a1e2898 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kxv_Latn_IN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kxv_Orya.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kxv_Orya.dat new file mode 100644 index 00000000..98172abd Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kxv_Orya.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kxv_Orya_IN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kxv_Orya_IN.dat new file mode 100644 index 00000000..8a1e2898 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kxv_Orya_IN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kxv_Telu.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kxv_Telu.dat new file mode 100644 index 00000000..1b3ac8b8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kxv_Telu.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/kxv_Telu_IN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/kxv_Telu_IN.dat new file mode 100644 index 00000000..8a1e2898 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/kxv_Telu_IN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ky.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ky.dat new file mode 100644 index 00000000..5bd19e57 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ky.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ky_KG.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ky_KG.dat new file mode 100644 index 00000000..c61073fe Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ky_KG.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/la.dat b/venv/lib/python3.12/site-packages/babel/locale-data/la.dat new file mode 100644 index 00000000..36656dc1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/la.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/la_VA.dat b/venv/lib/python3.12/site-packages/babel/locale-data/la_VA.dat new file mode 100644 index 00000000..31a68ac9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/la_VA.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/lag.dat b/venv/lib/python3.12/site-packages/babel/locale-data/lag.dat new file mode 100644 index 00000000..f8da045f Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/lag.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/lag_TZ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/lag_TZ.dat new file mode 100644 index 00000000..7893a0ed Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/lag_TZ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/lb.dat b/venv/lib/python3.12/site-packages/babel/locale-data/lb.dat new file mode 100644 index 00000000..0d3dc7d0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/lb.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/lb_LU.dat b/venv/lib/python3.12/site-packages/babel/locale-data/lb_LU.dat new file mode 100644 index 00000000..bcb56532 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/lb_LU.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/lg.dat b/venv/lib/python3.12/site-packages/babel/locale-data/lg.dat new file mode 100644 index 00000000..cd4e5a9d Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/lg.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/lg_UG.dat b/venv/lib/python3.12/site-packages/babel/locale-data/lg_UG.dat new file mode 100644 index 00000000..6ee321dc Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/lg_UG.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/lij.dat b/venv/lib/python3.12/site-packages/babel/locale-data/lij.dat new file mode 100644 index 00000000..8a7414e9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/lij.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/lij_IT.dat b/venv/lib/python3.12/site-packages/babel/locale-data/lij_IT.dat new file mode 100644 index 00000000..6969b528 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/lij_IT.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/lkt.dat b/venv/lib/python3.12/site-packages/babel/locale-data/lkt.dat new file mode 100644 index 00000000..515ac6c4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/lkt.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/lkt_US.dat b/venv/lib/python3.12/site-packages/babel/locale-data/lkt_US.dat new file mode 100644 index 00000000..44a5969a Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/lkt_US.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/lld.dat b/venv/lib/python3.12/site-packages/babel/locale-data/lld.dat new file mode 100644 index 00000000..04c08068 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/lld.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/lld_IT.dat b/venv/lib/python3.12/site-packages/babel/locale-data/lld_IT.dat new file mode 100644 index 00000000..f8d12f0f Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/lld_IT.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/lmo.dat b/venv/lib/python3.12/site-packages/babel/locale-data/lmo.dat new file mode 100644 index 00000000..c7388c7d Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/lmo.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/lmo_IT.dat b/venv/lib/python3.12/site-packages/babel/locale-data/lmo_IT.dat new file mode 100644 index 00000000..50dd5927 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/lmo_IT.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ln.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ln.dat new file mode 100644 index 00000000..cc8edb15 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ln.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ln_AO.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ln_AO.dat new file mode 100644 index 00000000..60dd0ba8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ln_AO.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ln_CD.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ln_CD.dat new file mode 100644 index 00000000..b87aad7d Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ln_CD.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ln_CF.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ln_CF.dat new file mode 100644 index 00000000..b5a9a827 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ln_CF.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ln_CG.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ln_CG.dat new file mode 100644 index 00000000..4059b369 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ln_CG.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/lo.dat b/venv/lib/python3.12/site-packages/babel/locale-data/lo.dat new file mode 100644 index 00000000..754556b8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/lo.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/lo_LA.dat b/venv/lib/python3.12/site-packages/babel/locale-data/lo_LA.dat new file mode 100644 index 00000000..6f12ab55 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/lo_LA.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/lrc.dat b/venv/lib/python3.12/site-packages/babel/locale-data/lrc.dat new file mode 100644 index 00000000..4452aa52 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/lrc.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/lrc_IQ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/lrc_IQ.dat new file mode 100644 index 00000000..b7fdaf27 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/lrc_IQ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/lrc_IR.dat b/venv/lib/python3.12/site-packages/babel/locale-data/lrc_IR.dat new file mode 100644 index 00000000..75d89752 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/lrc_IR.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/lt.dat b/venv/lib/python3.12/site-packages/babel/locale-data/lt.dat new file mode 100644 index 00000000..3c64a6d8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/lt.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/lt_LT.dat b/venv/lib/python3.12/site-packages/babel/locale-data/lt_LT.dat new file mode 100644 index 00000000..abd28f77 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/lt_LT.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ltg.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ltg.dat new file mode 100644 index 00000000..8937b943 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ltg.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ltg_LV.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ltg_LV.dat new file mode 100644 index 00000000..6afb1517 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ltg_LV.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/lu.dat b/venv/lib/python3.12/site-packages/babel/locale-data/lu.dat new file mode 100644 index 00000000..438bb41b Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/lu.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/lu_CD.dat b/venv/lib/python3.12/site-packages/babel/locale-data/lu_CD.dat new file mode 100644 index 00000000..b2f405cc Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/lu_CD.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/luo.dat b/venv/lib/python3.12/site-packages/babel/locale-data/luo.dat new file mode 100644 index 00000000..1fbbe68e Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/luo.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/luo_KE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/luo_KE.dat new file mode 100644 index 00000000..9ad4a6a4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/luo_KE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/luy.dat b/venv/lib/python3.12/site-packages/babel/locale-data/luy.dat new file mode 100644 index 00000000..c82c8929 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/luy.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/luy_KE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/luy_KE.dat new file mode 100644 index 00000000..e1b346e4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/luy_KE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/lv.dat b/venv/lib/python3.12/site-packages/babel/locale-data/lv.dat new file mode 100644 index 00000000..9d4a804a Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/lv.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/lv_LV.dat b/venv/lib/python3.12/site-packages/babel/locale-data/lv_LV.dat new file mode 100644 index 00000000..37bb4e26 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/lv_LV.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/mai.dat b/venv/lib/python3.12/site-packages/babel/locale-data/mai.dat new file mode 100644 index 00000000..bde3b353 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/mai.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/mai_IN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/mai_IN.dat new file mode 100644 index 00000000..288b92f8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/mai_IN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/mas.dat b/venv/lib/python3.12/site-packages/babel/locale-data/mas.dat new file mode 100644 index 00000000..7e254ed9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/mas.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/mas_KE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/mas_KE.dat new file mode 100644 index 00000000..b264c837 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/mas_KE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/mas_TZ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/mas_TZ.dat new file mode 100644 index 00000000..d5c71c37 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/mas_TZ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/mdf.dat b/venv/lib/python3.12/site-packages/babel/locale-data/mdf.dat new file mode 100644 index 00000000..5d4d3f83 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/mdf.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/mdf_RU.dat b/venv/lib/python3.12/site-packages/babel/locale-data/mdf_RU.dat new file mode 100644 index 00000000..94571359 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/mdf_RU.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/mer.dat b/venv/lib/python3.12/site-packages/babel/locale-data/mer.dat new file mode 100644 index 00000000..f6657fe1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/mer.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/mer_KE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/mer_KE.dat new file mode 100644 index 00000000..a7c286f7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/mer_KE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/mfe.dat b/venv/lib/python3.12/site-packages/babel/locale-data/mfe.dat new file mode 100644 index 00000000..10eef776 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/mfe.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/mfe_MU.dat b/venv/lib/python3.12/site-packages/babel/locale-data/mfe_MU.dat new file mode 100644 index 00000000..2f4715ba Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/mfe_MU.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/mg.dat b/venv/lib/python3.12/site-packages/babel/locale-data/mg.dat new file mode 100644 index 00000000..f91a1dd1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/mg.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/mg_MG.dat b/venv/lib/python3.12/site-packages/babel/locale-data/mg_MG.dat new file mode 100644 index 00000000..9e18e6cb Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/mg_MG.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/mgh.dat b/venv/lib/python3.12/site-packages/babel/locale-data/mgh.dat new file mode 100644 index 00000000..e8a28060 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/mgh.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/mgh_MZ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/mgh_MZ.dat new file mode 100644 index 00000000..999e8efb Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/mgh_MZ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/mgo.dat b/venv/lib/python3.12/site-packages/babel/locale-data/mgo.dat new file mode 100644 index 00000000..edbda979 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/mgo.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/mgo_CM.dat b/venv/lib/python3.12/site-packages/babel/locale-data/mgo_CM.dat new file mode 100644 index 00000000..667c0172 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/mgo_CM.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/mhn.dat b/venv/lib/python3.12/site-packages/babel/locale-data/mhn.dat new file mode 100644 index 00000000..1a3303d9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/mhn.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/mhn_IT.dat b/venv/lib/python3.12/site-packages/babel/locale-data/mhn_IT.dat new file mode 100644 index 00000000..c6472055 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/mhn_IT.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/mi.dat b/venv/lib/python3.12/site-packages/babel/locale-data/mi.dat new file mode 100644 index 00000000..79eb095a Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/mi.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/mi_NZ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/mi_NZ.dat new file mode 100644 index 00000000..7c8b4d19 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/mi_NZ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/mic.dat b/venv/lib/python3.12/site-packages/babel/locale-data/mic.dat new file mode 100644 index 00000000..3ca1892e Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/mic.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/mic_CA.dat b/venv/lib/python3.12/site-packages/babel/locale-data/mic_CA.dat new file mode 100644 index 00000000..f43465a9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/mic_CA.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/mk.dat b/venv/lib/python3.12/site-packages/babel/locale-data/mk.dat new file mode 100644 index 00000000..81cc0072 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/mk.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/mk_MK.dat b/venv/lib/python3.12/site-packages/babel/locale-data/mk_MK.dat new file mode 100644 index 00000000..33207d9a Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/mk_MK.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ml.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ml.dat new file mode 100644 index 00000000..f5e230e3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ml.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ml_IN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ml_IN.dat new file mode 100644 index 00000000..5f89f111 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ml_IN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/mn.dat b/venv/lib/python3.12/site-packages/babel/locale-data/mn.dat new file mode 100644 index 00000000..e314523d Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/mn.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/mn_MN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/mn_MN.dat new file mode 100644 index 00000000..096cb104 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/mn_MN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/mn_Mong.dat b/venv/lib/python3.12/site-packages/babel/locale-data/mn_Mong.dat new file mode 100644 index 00000000..287f8526 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/mn_Mong.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/mn_Mong_CN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/mn_Mong_CN.dat new file mode 100644 index 00000000..284cbee8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/mn_Mong_CN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/mn_Mong_MN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/mn_Mong_MN.dat new file mode 100644 index 00000000..ad54681c Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/mn_Mong_MN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/mni.dat b/venv/lib/python3.12/site-packages/babel/locale-data/mni.dat new file mode 100644 index 00000000..4ebcfc59 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/mni.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/mni_Beng.dat b/venv/lib/python3.12/site-packages/babel/locale-data/mni_Beng.dat new file mode 100644 index 00000000..de357a87 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/mni_Beng.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/mni_Beng_IN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/mni_Beng_IN.dat new file mode 100644 index 00000000..28787ccd Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/mni_Beng_IN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/mni_Mtei.dat b/venv/lib/python3.12/site-packages/babel/locale-data/mni_Mtei.dat new file mode 100644 index 00000000..654042f8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/mni_Mtei.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/mni_Mtei_IN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/mni_Mtei_IN.dat new file mode 100644 index 00000000..28787ccd Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/mni_Mtei_IN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/moh.dat b/venv/lib/python3.12/site-packages/babel/locale-data/moh.dat new file mode 100644 index 00000000..c301f3f7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/moh.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/moh_CA.dat b/venv/lib/python3.12/site-packages/babel/locale-data/moh_CA.dat new file mode 100644 index 00000000..ba2eddea Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/moh_CA.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/mr.dat b/venv/lib/python3.12/site-packages/babel/locale-data/mr.dat new file mode 100644 index 00000000..2c7d0d47 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/mr.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/mr_IN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/mr_IN.dat new file mode 100644 index 00000000..a57af72d Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/mr_IN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ms.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ms.dat new file mode 100644 index 00000000..b7fed4f6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ms.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ms_Arab.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ms_Arab.dat new file mode 100644 index 00000000..7ee5bee0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ms_Arab.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ms_Arab_BN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ms_Arab_BN.dat new file mode 100644 index 00000000..1785baa7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ms_Arab_BN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ms_Arab_MY.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ms_Arab_MY.dat new file mode 100644 index 00000000..5df0cb75 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ms_Arab_MY.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ms_BN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ms_BN.dat new file mode 100644 index 00000000..1785baa7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ms_BN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ms_ID.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ms_ID.dat new file mode 100644 index 00000000..07b0e769 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ms_ID.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ms_MY.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ms_MY.dat new file mode 100644 index 00000000..5df0cb75 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ms_MY.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ms_SG.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ms_SG.dat new file mode 100644 index 00000000..33205113 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ms_SG.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/mt.dat b/venv/lib/python3.12/site-packages/babel/locale-data/mt.dat new file mode 100644 index 00000000..502594b2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/mt.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/mt_MT.dat b/venv/lib/python3.12/site-packages/babel/locale-data/mt_MT.dat new file mode 100644 index 00000000..9f4731e4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/mt_MT.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/mua.dat b/venv/lib/python3.12/site-packages/babel/locale-data/mua.dat new file mode 100644 index 00000000..7d28adf1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/mua.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/mua_CM.dat b/venv/lib/python3.12/site-packages/babel/locale-data/mua_CM.dat new file mode 100644 index 00000000..5b71a89b Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/mua_CM.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/mus.dat b/venv/lib/python3.12/site-packages/babel/locale-data/mus.dat new file mode 100644 index 00000000..b323269d Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/mus.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/mus_US.dat b/venv/lib/python3.12/site-packages/babel/locale-data/mus_US.dat new file mode 100644 index 00000000..a506b8bd Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/mus_US.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/my.dat b/venv/lib/python3.12/site-packages/babel/locale-data/my.dat new file mode 100644 index 00000000..8bb745b2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/my.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/my_MM.dat b/venv/lib/python3.12/site-packages/babel/locale-data/my_MM.dat new file mode 100644 index 00000000..33bc9111 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/my_MM.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/myv.dat b/venv/lib/python3.12/site-packages/babel/locale-data/myv.dat new file mode 100644 index 00000000..896bdd71 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/myv.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/myv_RU.dat b/venv/lib/python3.12/site-packages/babel/locale-data/myv_RU.dat new file mode 100644 index 00000000..de64fee6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/myv_RU.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/mzn.dat b/venv/lib/python3.12/site-packages/babel/locale-data/mzn.dat new file mode 100644 index 00000000..51d9f132 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/mzn.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/mzn_IR.dat b/venv/lib/python3.12/site-packages/babel/locale-data/mzn_IR.dat new file mode 100644 index 00000000..73990744 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/mzn_IR.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/naq.dat b/venv/lib/python3.12/site-packages/babel/locale-data/naq.dat new file mode 100644 index 00000000..786b2ffe Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/naq.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/naq_NA.dat b/venv/lib/python3.12/site-packages/babel/locale-data/naq_NA.dat new file mode 100644 index 00000000..82c109a0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/naq_NA.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/nb.dat b/venv/lib/python3.12/site-packages/babel/locale-data/nb.dat new file mode 100644 index 00000000..5abc2536 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/nb.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/nb_NO.dat b/venv/lib/python3.12/site-packages/babel/locale-data/nb_NO.dat new file mode 100644 index 00000000..fc57a39a Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/nb_NO.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/nb_SJ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/nb_SJ.dat new file mode 100644 index 00000000..9af3a275 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/nb_SJ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/nd.dat b/venv/lib/python3.12/site-packages/babel/locale-data/nd.dat new file mode 100644 index 00000000..c13781ab Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/nd.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/nd_ZW.dat b/venv/lib/python3.12/site-packages/babel/locale-data/nd_ZW.dat new file mode 100644 index 00000000..bb86d3f6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/nd_ZW.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/nds.dat b/venv/lib/python3.12/site-packages/babel/locale-data/nds.dat new file mode 100644 index 00000000..e1c76cae Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/nds.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/nds_DE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/nds_DE.dat new file mode 100644 index 00000000..771fba3e Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/nds_DE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/nds_NL.dat b/venv/lib/python3.12/site-packages/babel/locale-data/nds_NL.dat new file mode 100644 index 00000000..14b9b123 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/nds_NL.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ne.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ne.dat new file mode 100644 index 00000000..228d625f Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ne.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ne_IN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ne_IN.dat new file mode 100644 index 00000000..647efe6d Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ne_IN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ne_NP.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ne_NP.dat new file mode 100644 index 00000000..5c513db9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ne_NP.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/nl.dat b/venv/lib/python3.12/site-packages/babel/locale-data/nl.dat new file mode 100644 index 00000000..9284bd74 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/nl.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/nl_AW.dat b/venv/lib/python3.12/site-packages/babel/locale-data/nl_AW.dat new file mode 100644 index 00000000..0bd826c4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/nl_AW.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/nl_BE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/nl_BE.dat new file mode 100644 index 00000000..4023b7a2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/nl_BE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/nl_BQ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/nl_BQ.dat new file mode 100644 index 00000000..8f686f79 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/nl_BQ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/nl_CW.dat b/venv/lib/python3.12/site-packages/babel/locale-data/nl_CW.dat new file mode 100644 index 00000000..ea3eb94f Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/nl_CW.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/nl_NL.dat b/venv/lib/python3.12/site-packages/babel/locale-data/nl_NL.dat new file mode 100644 index 00000000..c686473e Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/nl_NL.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/nl_SR.dat b/venv/lib/python3.12/site-packages/babel/locale-data/nl_SR.dat new file mode 100644 index 00000000..6ffb0f92 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/nl_SR.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/nl_SX.dat b/venv/lib/python3.12/site-packages/babel/locale-data/nl_SX.dat new file mode 100644 index 00000000..6932aa19 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/nl_SX.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/nmg.dat b/venv/lib/python3.12/site-packages/babel/locale-data/nmg.dat new file mode 100644 index 00000000..1e508106 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/nmg.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/nmg_CM.dat b/venv/lib/python3.12/site-packages/babel/locale-data/nmg_CM.dat new file mode 100644 index 00000000..f675485d Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/nmg_CM.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/nn.dat b/venv/lib/python3.12/site-packages/babel/locale-data/nn.dat new file mode 100644 index 00000000..fde5bfc9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/nn.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/nn_NO.dat b/venv/lib/python3.12/site-packages/babel/locale-data/nn_NO.dat new file mode 100644 index 00000000..0e233b7d Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/nn_NO.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/nnh.dat b/venv/lib/python3.12/site-packages/babel/locale-data/nnh.dat new file mode 100644 index 00000000..23896790 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/nnh.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/nnh_CM.dat b/venv/lib/python3.12/site-packages/babel/locale-data/nnh_CM.dat new file mode 100644 index 00000000..7722c099 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/nnh_CM.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/no.dat b/venv/lib/python3.12/site-packages/babel/locale-data/no.dat new file mode 100644 index 00000000..6fad909d Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/no.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/nqo.dat b/venv/lib/python3.12/site-packages/babel/locale-data/nqo.dat new file mode 100644 index 00000000..1f24a4ec Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/nqo.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/nqo_GN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/nqo_GN.dat new file mode 100644 index 00000000..3cdbcbcd Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/nqo_GN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/nr.dat b/venv/lib/python3.12/site-packages/babel/locale-data/nr.dat new file mode 100644 index 00000000..1306c925 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/nr.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/nr_ZA.dat b/venv/lib/python3.12/site-packages/babel/locale-data/nr_ZA.dat new file mode 100644 index 00000000..ff0ba0b0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/nr_ZA.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/nso.dat b/venv/lib/python3.12/site-packages/babel/locale-data/nso.dat new file mode 100644 index 00000000..25529c1a Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/nso.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/nso_ZA.dat b/venv/lib/python3.12/site-packages/babel/locale-data/nso_ZA.dat new file mode 100644 index 00000000..42f9efca Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/nso_ZA.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/nus.dat b/venv/lib/python3.12/site-packages/babel/locale-data/nus.dat new file mode 100644 index 00000000..6bc93828 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/nus.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/nus_SS.dat b/venv/lib/python3.12/site-packages/babel/locale-data/nus_SS.dat new file mode 100644 index 00000000..b8b86575 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/nus_SS.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/nv.dat b/venv/lib/python3.12/site-packages/babel/locale-data/nv.dat new file mode 100644 index 00000000..146e037b Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/nv.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/nv_US.dat b/venv/lib/python3.12/site-packages/babel/locale-data/nv_US.dat new file mode 100644 index 00000000..2c78aa76 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/nv_US.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ny.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ny.dat new file mode 100644 index 00000000..e1fc0613 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ny.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ny_MW.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ny_MW.dat new file mode 100644 index 00000000..00392558 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ny_MW.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/nyn.dat b/venv/lib/python3.12/site-packages/babel/locale-data/nyn.dat new file mode 100644 index 00000000..ccd34096 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/nyn.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/nyn_UG.dat b/venv/lib/python3.12/site-packages/babel/locale-data/nyn_UG.dat new file mode 100644 index 00000000..2e51c77b Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/nyn_UG.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/oc.dat b/venv/lib/python3.12/site-packages/babel/locale-data/oc.dat new file mode 100644 index 00000000..b678c283 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/oc.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/oc_ES.dat b/venv/lib/python3.12/site-packages/babel/locale-data/oc_ES.dat new file mode 100644 index 00000000..936a4bac Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/oc_ES.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/oc_FR.dat b/venv/lib/python3.12/site-packages/babel/locale-data/oc_FR.dat new file mode 100644 index 00000000..613abbe7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/oc_FR.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/om.dat b/venv/lib/python3.12/site-packages/babel/locale-data/om.dat new file mode 100644 index 00000000..c83455ff Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/om.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/om_ET.dat b/venv/lib/python3.12/site-packages/babel/locale-data/om_ET.dat new file mode 100644 index 00000000..82c438b4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/om_ET.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/om_KE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/om_KE.dat new file mode 100644 index 00000000..daf3734d Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/om_KE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/or.dat b/venv/lib/python3.12/site-packages/babel/locale-data/or.dat new file mode 100644 index 00000000..7368025a Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/or.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/or_IN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/or_IN.dat new file mode 100644 index 00000000..12b4f1bc Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/or_IN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/os.dat b/venv/lib/python3.12/site-packages/babel/locale-data/os.dat new file mode 100644 index 00000000..a90e8198 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/os.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/os_GE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/os_GE.dat new file mode 100644 index 00000000..205ddbf2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/os_GE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/os_RU.dat b/venv/lib/python3.12/site-packages/babel/locale-data/os_RU.dat new file mode 100644 index 00000000..f51d3d91 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/os_RU.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/osa.dat b/venv/lib/python3.12/site-packages/babel/locale-data/osa.dat new file mode 100644 index 00000000..eb1d4ff4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/osa.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/osa_US.dat b/venv/lib/python3.12/site-packages/babel/locale-data/osa_US.dat new file mode 100644 index 00000000..03c11002 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/osa_US.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/pa.dat b/venv/lib/python3.12/site-packages/babel/locale-data/pa.dat new file mode 100644 index 00000000..1186ec2f Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/pa.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/pa_Arab.dat b/venv/lib/python3.12/site-packages/babel/locale-data/pa_Arab.dat new file mode 100644 index 00000000..0d02acbd Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/pa_Arab.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/pa_Arab_PK.dat b/venv/lib/python3.12/site-packages/babel/locale-data/pa_Arab_PK.dat new file mode 100644 index 00000000..eefdeba1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/pa_Arab_PK.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/pa_Guru.dat b/venv/lib/python3.12/site-packages/babel/locale-data/pa_Guru.dat new file mode 100644 index 00000000..ed8c6b6f Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/pa_Guru.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/pa_Guru_IN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/pa_Guru_IN.dat new file mode 100644 index 00000000..6af3517d Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/pa_Guru_IN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/pap.dat b/venv/lib/python3.12/site-packages/babel/locale-data/pap.dat new file mode 100644 index 00000000..68b8dc46 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/pap.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/pap_AW.dat b/venv/lib/python3.12/site-packages/babel/locale-data/pap_AW.dat new file mode 100644 index 00000000..973cc763 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/pap_AW.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/pap_CW.dat b/venv/lib/python3.12/site-packages/babel/locale-data/pap_CW.dat new file mode 100644 index 00000000..3c481d47 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/pap_CW.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/pcm.dat b/venv/lib/python3.12/site-packages/babel/locale-data/pcm.dat new file mode 100644 index 00000000..97447ca5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/pcm.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/pcm_NG.dat b/venv/lib/python3.12/site-packages/babel/locale-data/pcm_NG.dat new file mode 100644 index 00000000..7ee82746 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/pcm_NG.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/pis.dat b/venv/lib/python3.12/site-packages/babel/locale-data/pis.dat new file mode 100644 index 00000000..55726a02 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/pis.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/pis_SB.dat b/venv/lib/python3.12/site-packages/babel/locale-data/pis_SB.dat new file mode 100644 index 00000000..3b1f17cd Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/pis_SB.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/pl.dat b/venv/lib/python3.12/site-packages/babel/locale-data/pl.dat new file mode 100644 index 00000000..65ac0a48 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/pl.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/pl_PL.dat b/venv/lib/python3.12/site-packages/babel/locale-data/pl_PL.dat new file mode 100644 index 00000000..7a4339ab Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/pl_PL.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/prg.dat b/venv/lib/python3.12/site-packages/babel/locale-data/prg.dat new file mode 100644 index 00000000..06219914 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/prg.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/prg_PL.dat b/venv/lib/python3.12/site-packages/babel/locale-data/prg_PL.dat new file mode 100644 index 00000000..7b197051 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/prg_PL.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ps.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ps.dat new file mode 100644 index 00000000..ea7e2e52 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ps.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ps_AF.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ps_AF.dat new file mode 100644 index 00000000..94a50bff Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ps_AF.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ps_PK.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ps_PK.dat new file mode 100644 index 00000000..691557ea Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ps_PK.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/pt.dat b/venv/lib/python3.12/site-packages/babel/locale-data/pt.dat new file mode 100644 index 00000000..aefae529 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/pt.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/pt_AO.dat b/venv/lib/python3.12/site-packages/babel/locale-data/pt_AO.dat new file mode 100644 index 00000000..4cd1fbda Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/pt_AO.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/pt_BR.dat b/venv/lib/python3.12/site-packages/babel/locale-data/pt_BR.dat new file mode 100644 index 00000000..22952c77 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/pt_BR.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/pt_CH.dat b/venv/lib/python3.12/site-packages/babel/locale-data/pt_CH.dat new file mode 100644 index 00000000..26842865 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/pt_CH.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/pt_CV.dat b/venv/lib/python3.12/site-packages/babel/locale-data/pt_CV.dat new file mode 100644 index 00000000..78e2bbcf Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/pt_CV.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/pt_GQ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/pt_GQ.dat new file mode 100644 index 00000000..88968337 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/pt_GQ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/pt_GW.dat b/venv/lib/python3.12/site-packages/babel/locale-data/pt_GW.dat new file mode 100644 index 00000000..d44108a1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/pt_GW.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/pt_LU.dat b/venv/lib/python3.12/site-packages/babel/locale-data/pt_LU.dat new file mode 100644 index 00000000..af0b45b3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/pt_LU.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/pt_MO.dat b/venv/lib/python3.12/site-packages/babel/locale-data/pt_MO.dat new file mode 100644 index 00000000..4fcee099 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/pt_MO.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/pt_MZ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/pt_MZ.dat new file mode 100644 index 00000000..198075e8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/pt_MZ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/pt_PT.dat b/venv/lib/python3.12/site-packages/babel/locale-data/pt_PT.dat new file mode 100644 index 00000000..aa6a6ccb Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/pt_PT.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/pt_ST.dat b/venv/lib/python3.12/site-packages/babel/locale-data/pt_ST.dat new file mode 100644 index 00000000..cdccab77 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/pt_ST.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/pt_TL.dat b/venv/lib/python3.12/site-packages/babel/locale-data/pt_TL.dat new file mode 100644 index 00000000..7bb756cd Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/pt_TL.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/qu.dat b/venv/lib/python3.12/site-packages/babel/locale-data/qu.dat new file mode 100644 index 00000000..85cc9eca Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/qu.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/qu_BO.dat b/venv/lib/python3.12/site-packages/babel/locale-data/qu_BO.dat new file mode 100644 index 00000000..bc994ff7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/qu_BO.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/qu_EC.dat b/venv/lib/python3.12/site-packages/babel/locale-data/qu_EC.dat new file mode 100644 index 00000000..8f7f46ad Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/qu_EC.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/qu_PE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/qu_PE.dat new file mode 100644 index 00000000..07aacf94 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/qu_PE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/quc.dat b/venv/lib/python3.12/site-packages/babel/locale-data/quc.dat new file mode 100644 index 00000000..f7e02d8a Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/quc.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/quc_GT.dat b/venv/lib/python3.12/site-packages/babel/locale-data/quc_GT.dat new file mode 100644 index 00000000..c14f849c Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/quc_GT.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/raj.dat b/venv/lib/python3.12/site-packages/babel/locale-data/raj.dat new file mode 100644 index 00000000..8f4477fa Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/raj.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/raj_IN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/raj_IN.dat new file mode 100644 index 00000000..e56e7502 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/raj_IN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/rhg.dat b/venv/lib/python3.12/site-packages/babel/locale-data/rhg.dat new file mode 100644 index 00000000..192bbff0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/rhg.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/rhg_Rohg.dat b/venv/lib/python3.12/site-packages/babel/locale-data/rhg_Rohg.dat new file mode 100644 index 00000000..01b82ecf Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/rhg_Rohg.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/rhg_Rohg_BD.dat b/venv/lib/python3.12/site-packages/babel/locale-data/rhg_Rohg_BD.dat new file mode 100644 index 00000000..cc14c7f8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/rhg_Rohg_BD.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/rhg_Rohg_MM.dat b/venv/lib/python3.12/site-packages/babel/locale-data/rhg_Rohg_MM.dat new file mode 100644 index 00000000..6cfde861 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/rhg_Rohg_MM.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/rif.dat b/venv/lib/python3.12/site-packages/babel/locale-data/rif.dat new file mode 100644 index 00000000..b5efc905 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/rif.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/rif_MA.dat b/venv/lib/python3.12/site-packages/babel/locale-data/rif_MA.dat new file mode 100644 index 00000000..9b7f4342 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/rif_MA.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/rm.dat b/venv/lib/python3.12/site-packages/babel/locale-data/rm.dat new file mode 100644 index 00000000..2398d907 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/rm.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/rm_CH.dat b/venv/lib/python3.12/site-packages/babel/locale-data/rm_CH.dat new file mode 100644 index 00000000..26eccfc3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/rm_CH.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/rn.dat b/venv/lib/python3.12/site-packages/babel/locale-data/rn.dat new file mode 100644 index 00000000..f21edd4d Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/rn.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/rn_BI.dat b/venv/lib/python3.12/site-packages/babel/locale-data/rn_BI.dat new file mode 100644 index 00000000..d1ef9755 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/rn_BI.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ro.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ro.dat new file mode 100644 index 00000000..efa69b04 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ro.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ro_MD.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ro_MD.dat new file mode 100644 index 00000000..82b8bb6d Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ro_MD.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ro_RO.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ro_RO.dat new file mode 100644 index 00000000..3517a610 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ro_RO.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/rof.dat b/venv/lib/python3.12/site-packages/babel/locale-data/rof.dat new file mode 100644 index 00000000..6f0e5514 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/rof.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/rof_TZ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/rof_TZ.dat new file mode 100644 index 00000000..d7ffaeb0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/rof_TZ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/root.dat b/venv/lib/python3.12/site-packages/babel/locale-data/root.dat new file mode 100644 index 00000000..0557e0d2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/root.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ru.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ru.dat new file mode 100644 index 00000000..50c78459 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ru.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ru_BY.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ru_BY.dat new file mode 100644 index 00000000..e8ef98d4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ru_BY.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ru_KG.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ru_KG.dat new file mode 100644 index 00000000..f3e59502 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ru_KG.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ru_KZ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ru_KZ.dat new file mode 100644 index 00000000..b66765a5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ru_KZ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ru_MD.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ru_MD.dat new file mode 100644 index 00000000..d7041994 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ru_MD.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ru_RU.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ru_RU.dat new file mode 100644 index 00000000..a1183570 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ru_RU.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ru_UA.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ru_UA.dat new file mode 100644 index 00000000..cebd8c23 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ru_UA.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/rw.dat b/venv/lib/python3.12/site-packages/babel/locale-data/rw.dat new file mode 100644 index 00000000..d767b309 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/rw.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/rw_RW.dat b/venv/lib/python3.12/site-packages/babel/locale-data/rw_RW.dat new file mode 100644 index 00000000..4ade0dd6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/rw_RW.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/rwk.dat b/venv/lib/python3.12/site-packages/babel/locale-data/rwk.dat new file mode 100644 index 00000000..fd69cc07 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/rwk.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/rwk_TZ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/rwk_TZ.dat new file mode 100644 index 00000000..fedafc3e Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/rwk_TZ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sa.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sa.dat new file mode 100644 index 00000000..76f67c4d Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sa.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sa_IN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sa_IN.dat new file mode 100644 index 00000000..31781751 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sa_IN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sah.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sah.dat new file mode 100644 index 00000000..bad972f9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sah.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sah_RU.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sah_RU.dat new file mode 100644 index 00000000..44075f9f Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sah_RU.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/saq.dat b/venv/lib/python3.12/site-packages/babel/locale-data/saq.dat new file mode 100644 index 00000000..95320833 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/saq.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/saq_KE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/saq_KE.dat new file mode 100644 index 00000000..ffd91c35 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/saq_KE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sat.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sat.dat new file mode 100644 index 00000000..3a63c743 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sat.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sat_Deva.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sat_Deva.dat new file mode 100644 index 00000000..9a1af369 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sat_Deva.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sat_Deva_IN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sat_Deva_IN.dat new file mode 100644 index 00000000..1781be05 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sat_Deva_IN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sat_Olck.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sat_Olck.dat new file mode 100644 index 00000000..444b3584 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sat_Olck.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sat_Olck_IN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sat_Olck_IN.dat new file mode 100644 index 00000000..1781be05 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sat_Olck_IN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sbp.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sbp.dat new file mode 100644 index 00000000..e1ea58b9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sbp.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sbp_TZ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sbp_TZ.dat new file mode 100644 index 00000000..2292ffb6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sbp_TZ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sc.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sc.dat new file mode 100644 index 00000000..f3609d5c Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sc.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sc_IT.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sc_IT.dat new file mode 100644 index 00000000..12c8c563 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sc_IT.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/scn.dat b/venv/lib/python3.12/site-packages/babel/locale-data/scn.dat new file mode 100644 index 00000000..c8ebaec1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/scn.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/scn_IT.dat b/venv/lib/python3.12/site-packages/babel/locale-data/scn_IT.dat new file mode 100644 index 00000000..0e8b90ec Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/scn_IT.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sd.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sd.dat new file mode 100644 index 00000000..410ea581 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sd.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sd_Arab.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sd_Arab.dat new file mode 100644 index 00000000..8003f26f Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sd_Arab.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sd_Arab_PK.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sd_Arab_PK.dat new file mode 100644 index 00000000..42f6290f Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sd_Arab_PK.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sd_Deva.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sd_Deva.dat new file mode 100644 index 00000000..5193db74 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sd_Deva.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sd_Deva_IN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sd_Deva_IN.dat new file mode 100644 index 00000000..21aee01c Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sd_Deva_IN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sdh.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sdh.dat new file mode 100644 index 00000000..f51767d1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sdh.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sdh_IQ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sdh_IQ.dat new file mode 100644 index 00000000..774d04d5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sdh_IQ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sdh_IR.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sdh_IR.dat new file mode 100644 index 00000000..6e83d1f1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sdh_IR.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/se.dat b/venv/lib/python3.12/site-packages/babel/locale-data/se.dat new file mode 100644 index 00000000..8da45922 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/se.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/se_FI.dat b/venv/lib/python3.12/site-packages/babel/locale-data/se_FI.dat new file mode 100644 index 00000000..2f927646 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/se_FI.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/se_NO.dat b/venv/lib/python3.12/site-packages/babel/locale-data/se_NO.dat new file mode 100644 index 00000000..09eca0d2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/se_NO.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/se_SE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/se_SE.dat new file mode 100644 index 00000000..632b54e7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/se_SE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/seh.dat b/venv/lib/python3.12/site-packages/babel/locale-data/seh.dat new file mode 100644 index 00000000..e61f09ba Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/seh.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/seh_MZ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/seh_MZ.dat new file mode 100644 index 00000000..ba30e958 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/seh_MZ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ses.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ses.dat new file mode 100644 index 00000000..f9858d96 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ses.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ses_ML.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ses_ML.dat new file mode 100644 index 00000000..a0010889 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ses_ML.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sg.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sg.dat new file mode 100644 index 00000000..8ce0d409 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sg.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sg_CF.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sg_CF.dat new file mode 100644 index 00000000..0ca555a6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sg_CF.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/shi.dat b/venv/lib/python3.12/site-packages/babel/locale-data/shi.dat new file mode 100644 index 00000000..f3d10667 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/shi.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/shi_Latn.dat b/venv/lib/python3.12/site-packages/babel/locale-data/shi_Latn.dat new file mode 100644 index 00000000..fecc8217 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/shi_Latn.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/shi_Latn_MA.dat b/venv/lib/python3.12/site-packages/babel/locale-data/shi_Latn_MA.dat new file mode 100644 index 00000000..14955853 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/shi_Latn_MA.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/shi_Tfng.dat b/venv/lib/python3.12/site-packages/babel/locale-data/shi_Tfng.dat new file mode 100644 index 00000000..63ebefe7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/shi_Tfng.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/shi_Tfng_MA.dat b/venv/lib/python3.12/site-packages/babel/locale-data/shi_Tfng_MA.dat new file mode 100644 index 00000000..14955853 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/shi_Tfng_MA.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/shn.dat b/venv/lib/python3.12/site-packages/babel/locale-data/shn.dat new file mode 100644 index 00000000..7eb56999 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/shn.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/shn_MM.dat b/venv/lib/python3.12/site-packages/babel/locale-data/shn_MM.dat new file mode 100644 index 00000000..3cace292 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/shn_MM.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/shn_TH.dat b/venv/lib/python3.12/site-packages/babel/locale-data/shn_TH.dat new file mode 100644 index 00000000..cc5364a2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/shn_TH.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/si.dat b/venv/lib/python3.12/site-packages/babel/locale-data/si.dat new file mode 100644 index 00000000..fba9aa9f Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/si.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/si_LK.dat b/venv/lib/python3.12/site-packages/babel/locale-data/si_LK.dat new file mode 100644 index 00000000..eb323029 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/si_LK.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sid.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sid.dat new file mode 100644 index 00000000..3445845d Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sid.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sid_ET.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sid_ET.dat new file mode 100644 index 00000000..1be7a4a9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sid_ET.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sk.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sk.dat new file mode 100644 index 00000000..413a7038 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sk.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sk_SK.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sk_SK.dat new file mode 100644 index 00000000..4ba6c841 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sk_SK.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/skr.dat b/venv/lib/python3.12/site-packages/babel/locale-data/skr.dat new file mode 100644 index 00000000..d9fc4290 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/skr.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/skr_PK.dat b/venv/lib/python3.12/site-packages/babel/locale-data/skr_PK.dat new file mode 100644 index 00000000..8db57bc5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/skr_PK.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sl.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sl.dat new file mode 100644 index 00000000..9e805fb2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sl.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sl_SI.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sl_SI.dat new file mode 100644 index 00000000..b4955823 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sl_SI.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sma.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sma.dat new file mode 100644 index 00000000..925c9565 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sma.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sma_NO.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sma_NO.dat new file mode 100644 index 00000000..325e16cc Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sma_NO.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sma_SE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sma_SE.dat new file mode 100644 index 00000000..b9cb4e75 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sma_SE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/smj.dat b/venv/lib/python3.12/site-packages/babel/locale-data/smj.dat new file mode 100644 index 00000000..377b17c7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/smj.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/smj_NO.dat b/venv/lib/python3.12/site-packages/babel/locale-data/smj_NO.dat new file mode 100644 index 00000000..7cc36547 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/smj_NO.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/smj_SE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/smj_SE.dat new file mode 100644 index 00000000..78aa9de9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/smj_SE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/smn.dat b/venv/lib/python3.12/site-packages/babel/locale-data/smn.dat new file mode 100644 index 00000000..f63177af Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/smn.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/smn_FI.dat b/venv/lib/python3.12/site-packages/babel/locale-data/smn_FI.dat new file mode 100644 index 00000000..a88e29d9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/smn_FI.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sms.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sms.dat new file mode 100644 index 00000000..590f25ad Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sms.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sms_FI.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sms_FI.dat new file mode 100644 index 00000000..91bdf45e Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sms_FI.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sn.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sn.dat new file mode 100644 index 00000000..cb8e1bd8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sn.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sn_ZW.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sn_ZW.dat new file mode 100644 index 00000000..4dd1932d Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sn_ZW.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/so.dat b/venv/lib/python3.12/site-packages/babel/locale-data/so.dat new file mode 100644 index 00000000..cfae4764 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/so.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/so_DJ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/so_DJ.dat new file mode 100644 index 00000000..756644b6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/so_DJ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/so_ET.dat b/venv/lib/python3.12/site-packages/babel/locale-data/so_ET.dat new file mode 100644 index 00000000..abd6c125 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/so_ET.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/so_KE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/so_KE.dat new file mode 100644 index 00000000..2c422f93 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/so_KE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/so_SO.dat b/venv/lib/python3.12/site-packages/babel/locale-data/so_SO.dat new file mode 100644 index 00000000..aa7955c5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/so_SO.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sq.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sq.dat new file mode 100644 index 00000000..aa0a941b Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sq.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sq_AL.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sq_AL.dat new file mode 100644 index 00000000..5a5320fc Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sq_AL.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sq_MK.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sq_MK.dat new file mode 100644 index 00000000..32565ce7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sq_MK.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sq_XK.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sq_XK.dat new file mode 100644 index 00000000..bbe0062e Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sq_XK.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sr.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sr.dat new file mode 100644 index 00000000..0075f8e1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sr.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sr_Cyrl.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sr_Cyrl.dat new file mode 100644 index 00000000..89154355 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sr_Cyrl.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sr_Cyrl_BA.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sr_Cyrl_BA.dat new file mode 100644 index 00000000..6a0aaf69 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sr_Cyrl_BA.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sr_Cyrl_ME.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sr_Cyrl_ME.dat new file mode 100644 index 00000000..357ab293 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sr_Cyrl_ME.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sr_Cyrl_RS.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sr_Cyrl_RS.dat new file mode 100644 index 00000000..b2633098 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sr_Cyrl_RS.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sr_Cyrl_XK.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sr_Cyrl_XK.dat new file mode 100644 index 00000000..b574d058 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sr_Cyrl_XK.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sr_Latn.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sr_Latn.dat new file mode 100644 index 00000000..efd38936 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sr_Latn.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sr_Latn_BA.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sr_Latn_BA.dat new file mode 100644 index 00000000..d1be6b89 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sr_Latn_BA.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sr_Latn_ME.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sr_Latn_ME.dat new file mode 100644 index 00000000..b8b9a5b4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sr_Latn_ME.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sr_Latn_RS.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sr_Latn_RS.dat new file mode 100644 index 00000000..b2633098 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sr_Latn_RS.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sr_Latn_XK.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sr_Latn_XK.dat new file mode 100644 index 00000000..778db676 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sr_Latn_XK.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ss.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ss.dat new file mode 100644 index 00000000..91c10e2c Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ss.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ss_SZ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ss_SZ.dat new file mode 100644 index 00000000..b4fcf779 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ss_SZ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ss_ZA.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ss_ZA.dat new file mode 100644 index 00000000..67315698 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ss_ZA.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ssy.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ssy.dat new file mode 100644 index 00000000..a42b5e83 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ssy.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ssy_ER.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ssy_ER.dat new file mode 100644 index 00000000..1d28855a Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ssy_ER.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/st.dat b/venv/lib/python3.12/site-packages/babel/locale-data/st.dat new file mode 100644 index 00000000..26d9b21a Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/st.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/st_LS.dat b/venv/lib/python3.12/site-packages/babel/locale-data/st_LS.dat new file mode 100644 index 00000000..50eef784 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/st_LS.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/st_ZA.dat b/venv/lib/python3.12/site-packages/babel/locale-data/st_ZA.dat new file mode 100644 index 00000000..9539d624 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/st_ZA.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/su.dat b/venv/lib/python3.12/site-packages/babel/locale-data/su.dat new file mode 100644 index 00000000..011036d5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/su.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/su_Latn.dat b/venv/lib/python3.12/site-packages/babel/locale-data/su_Latn.dat new file mode 100644 index 00000000..79e460ed Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/su_Latn.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/su_Latn_ID.dat b/venv/lib/python3.12/site-packages/babel/locale-data/su_Latn_ID.dat new file mode 100644 index 00000000..5c45bdce Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/su_Latn_ID.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sv.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sv.dat new file mode 100644 index 00000000..1bb77229 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sv.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sv_AX.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sv_AX.dat new file mode 100644 index 00000000..bfb414d6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sv_AX.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sv_FI.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sv_FI.dat new file mode 100644 index 00000000..49deaf31 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sv_FI.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sv_SE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sv_SE.dat new file mode 100644 index 00000000..57a602a0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sv_SE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sw.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sw.dat new file mode 100644 index 00000000..df6f886f Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sw.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sw_CD.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sw_CD.dat new file mode 100644 index 00000000..b1a7f0e5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sw_CD.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sw_KE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sw_KE.dat new file mode 100644 index 00000000..f19305af Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sw_KE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sw_TZ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sw_TZ.dat new file mode 100644 index 00000000..a267886d Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sw_TZ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/sw_UG.dat b/venv/lib/python3.12/site-packages/babel/locale-data/sw_UG.dat new file mode 100644 index 00000000..0d160d55 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/sw_UG.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/syr.dat b/venv/lib/python3.12/site-packages/babel/locale-data/syr.dat new file mode 100644 index 00000000..6cfeef61 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/syr.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/syr_IQ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/syr_IQ.dat new file mode 100644 index 00000000..7821e924 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/syr_IQ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/syr_SY.dat b/venv/lib/python3.12/site-packages/babel/locale-data/syr_SY.dat new file mode 100644 index 00000000..1edf041c Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/syr_SY.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/szl.dat b/venv/lib/python3.12/site-packages/babel/locale-data/szl.dat new file mode 100644 index 00000000..4a767b48 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/szl.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/szl_PL.dat b/venv/lib/python3.12/site-packages/babel/locale-data/szl_PL.dat new file mode 100644 index 00000000..f0b99548 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/szl_PL.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ta.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ta.dat new file mode 100644 index 00000000..7559ff89 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ta.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ta_IN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ta_IN.dat new file mode 100644 index 00000000..cd08ee8f Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ta_IN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ta_LK.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ta_LK.dat new file mode 100644 index 00000000..f473b057 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ta_LK.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ta_MY.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ta_MY.dat new file mode 100644 index 00000000..8e791e92 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ta_MY.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ta_SG.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ta_SG.dat new file mode 100644 index 00000000..9f1131cc Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ta_SG.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/te.dat b/venv/lib/python3.12/site-packages/babel/locale-data/te.dat new file mode 100644 index 00000000..436255df Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/te.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/te_IN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/te_IN.dat new file mode 100644 index 00000000..d76a4160 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/te_IN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/teo.dat b/venv/lib/python3.12/site-packages/babel/locale-data/teo.dat new file mode 100644 index 00000000..d4cfa1af Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/teo.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/teo_KE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/teo_KE.dat new file mode 100644 index 00000000..66b33cad Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/teo_KE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/teo_UG.dat b/venv/lib/python3.12/site-packages/babel/locale-data/teo_UG.dat new file mode 100644 index 00000000..e81befad Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/teo_UG.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/tg.dat b/venv/lib/python3.12/site-packages/babel/locale-data/tg.dat new file mode 100644 index 00000000..03a72512 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/tg.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/tg_TJ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/tg_TJ.dat new file mode 100644 index 00000000..1982383d Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/tg_TJ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/th.dat b/venv/lib/python3.12/site-packages/babel/locale-data/th.dat new file mode 100644 index 00000000..e6884133 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/th.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/th_TH.dat b/venv/lib/python3.12/site-packages/babel/locale-data/th_TH.dat new file mode 100644 index 00000000..c2a4c994 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/th_TH.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ti.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ti.dat new file mode 100644 index 00000000..ef311739 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ti.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ti_ER.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ti_ER.dat new file mode 100644 index 00000000..41ca554c Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ti_ER.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ti_ET.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ti_ET.dat new file mode 100644 index 00000000..50a447b5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ti_ET.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/tig.dat b/venv/lib/python3.12/site-packages/babel/locale-data/tig.dat new file mode 100644 index 00000000..ed1b2a79 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/tig.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/tig_ER.dat b/venv/lib/python3.12/site-packages/babel/locale-data/tig_ER.dat new file mode 100644 index 00000000..5632fa6f Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/tig_ER.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/tk.dat b/venv/lib/python3.12/site-packages/babel/locale-data/tk.dat new file mode 100644 index 00000000..46c4af00 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/tk.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/tk_TM.dat b/venv/lib/python3.12/site-packages/babel/locale-data/tk_TM.dat new file mode 100644 index 00000000..01e7cf61 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/tk_TM.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/tn.dat b/venv/lib/python3.12/site-packages/babel/locale-data/tn.dat new file mode 100644 index 00000000..65c0267f Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/tn.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/tn_BW.dat b/venv/lib/python3.12/site-packages/babel/locale-data/tn_BW.dat new file mode 100644 index 00000000..5da0cc5d Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/tn_BW.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/tn_ZA.dat b/venv/lib/python3.12/site-packages/babel/locale-data/tn_ZA.dat new file mode 100644 index 00000000..a559a128 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/tn_ZA.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/to.dat b/venv/lib/python3.12/site-packages/babel/locale-data/to.dat new file mode 100644 index 00000000..7268ff20 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/to.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/to_TO.dat b/venv/lib/python3.12/site-packages/babel/locale-data/to_TO.dat new file mode 100644 index 00000000..8cdedf55 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/to_TO.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/tok.dat b/venv/lib/python3.12/site-packages/babel/locale-data/tok.dat new file mode 100644 index 00000000..a8b7a886 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/tok.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/tok_001.dat b/venv/lib/python3.12/site-packages/babel/locale-data/tok_001.dat new file mode 100644 index 00000000..ea39a455 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/tok_001.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/tpi.dat b/venv/lib/python3.12/site-packages/babel/locale-data/tpi.dat new file mode 100644 index 00000000..0434b6fa Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/tpi.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/tpi_PG.dat b/venv/lib/python3.12/site-packages/babel/locale-data/tpi_PG.dat new file mode 100644 index 00000000..9057c6e4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/tpi_PG.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/tr.dat b/venv/lib/python3.12/site-packages/babel/locale-data/tr.dat new file mode 100644 index 00000000..1bd0819f Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/tr.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/tr_CY.dat b/venv/lib/python3.12/site-packages/babel/locale-data/tr_CY.dat new file mode 100644 index 00000000..a0bbd6f0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/tr_CY.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/tr_TR.dat b/venv/lib/python3.12/site-packages/babel/locale-data/tr_TR.dat new file mode 100644 index 00000000..8c20ebb6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/tr_TR.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/trv.dat b/venv/lib/python3.12/site-packages/babel/locale-data/trv.dat new file mode 100644 index 00000000..1b784bc8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/trv.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/trv_TW.dat b/venv/lib/python3.12/site-packages/babel/locale-data/trv_TW.dat new file mode 100644 index 00000000..da4e716d Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/trv_TW.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/trw.dat b/venv/lib/python3.12/site-packages/babel/locale-data/trw.dat new file mode 100644 index 00000000..e4a66bd8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/trw.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/trw_PK.dat b/venv/lib/python3.12/site-packages/babel/locale-data/trw_PK.dat new file mode 100644 index 00000000..593e957e Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/trw_PK.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ts.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ts.dat new file mode 100644 index 00000000..900383d3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ts.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ts_ZA.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ts_ZA.dat new file mode 100644 index 00000000..53640320 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ts_ZA.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/tt.dat b/venv/lib/python3.12/site-packages/babel/locale-data/tt.dat new file mode 100644 index 00000000..28fe370c Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/tt.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/tt_RU.dat b/venv/lib/python3.12/site-packages/babel/locale-data/tt_RU.dat new file mode 100644 index 00000000..88ba72f3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/tt_RU.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/twq.dat b/venv/lib/python3.12/site-packages/babel/locale-data/twq.dat new file mode 100644 index 00000000..2f99122f Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/twq.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/twq_NE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/twq_NE.dat new file mode 100644 index 00000000..be95079d Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/twq_NE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/tyv.dat b/venv/lib/python3.12/site-packages/babel/locale-data/tyv.dat new file mode 100644 index 00000000..fe1564b9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/tyv.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/tyv_RU.dat b/venv/lib/python3.12/site-packages/babel/locale-data/tyv_RU.dat new file mode 100644 index 00000000..92ab47b5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/tyv_RU.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/tzm.dat b/venv/lib/python3.12/site-packages/babel/locale-data/tzm.dat new file mode 100644 index 00000000..14f6276b Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/tzm.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/tzm_MA.dat b/venv/lib/python3.12/site-packages/babel/locale-data/tzm_MA.dat new file mode 100644 index 00000000..b74c9377 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/tzm_MA.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ug.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ug.dat new file mode 100644 index 00000000..28cd5d09 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ug.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ug_CN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ug_CN.dat new file mode 100644 index 00000000..997752b7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ug_CN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/uk.dat b/venv/lib/python3.12/site-packages/babel/locale-data/uk.dat new file mode 100644 index 00000000..908f4870 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/uk.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/uk_UA.dat b/venv/lib/python3.12/site-packages/babel/locale-data/uk_UA.dat new file mode 100644 index 00000000..e1ff80ca Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/uk_UA.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ur.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ur.dat new file mode 100644 index 00000000..dd035ccd Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ur.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ur_IN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ur_IN.dat new file mode 100644 index 00000000..582a1fc3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ur_IN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ur_PK.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ur_PK.dat new file mode 100644 index 00000000..5a3d6cec Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ur_PK.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/uz.dat b/venv/lib/python3.12/site-packages/babel/locale-data/uz.dat new file mode 100644 index 00000000..267dd64e Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/uz.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/uz_Arab.dat b/venv/lib/python3.12/site-packages/babel/locale-data/uz_Arab.dat new file mode 100644 index 00000000..0ed5c3ae Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/uz_Arab.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/uz_Arab_AF.dat b/venv/lib/python3.12/site-packages/babel/locale-data/uz_Arab_AF.dat new file mode 100644 index 00000000..d7355f66 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/uz_Arab_AF.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/uz_Cyrl.dat b/venv/lib/python3.12/site-packages/babel/locale-data/uz_Cyrl.dat new file mode 100644 index 00000000..382b3cc0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/uz_Cyrl.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/uz_Cyrl_UZ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/uz_Cyrl_UZ.dat new file mode 100644 index 00000000..0e9fb275 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/uz_Cyrl_UZ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/uz_Latn.dat b/venv/lib/python3.12/site-packages/babel/locale-data/uz_Latn.dat new file mode 100644 index 00000000..217235e4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/uz_Latn.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/uz_Latn_UZ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/uz_Latn_UZ.dat new file mode 100644 index 00000000..0e9fb275 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/uz_Latn_UZ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/vai.dat b/venv/lib/python3.12/site-packages/babel/locale-data/vai.dat new file mode 100644 index 00000000..c69f2cba Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/vai.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/vai_Latn.dat b/venv/lib/python3.12/site-packages/babel/locale-data/vai_Latn.dat new file mode 100644 index 00000000..849c491d Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/vai_Latn.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/vai_Latn_LR.dat b/venv/lib/python3.12/site-packages/babel/locale-data/vai_Latn_LR.dat new file mode 100644 index 00000000..f3bc7f03 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/vai_Latn_LR.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/vai_Vaii.dat b/venv/lib/python3.12/site-packages/babel/locale-data/vai_Vaii.dat new file mode 100644 index 00000000..6519e24e Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/vai_Vaii.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/vai_Vaii_LR.dat b/venv/lib/python3.12/site-packages/babel/locale-data/vai_Vaii_LR.dat new file mode 100644 index 00000000..f3bc7f03 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/vai_Vaii_LR.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ve.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ve.dat new file mode 100644 index 00000000..517c7a44 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ve.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/ve_ZA.dat b/venv/lib/python3.12/site-packages/babel/locale-data/ve_ZA.dat new file mode 100644 index 00000000..81c087b3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/ve_ZA.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/vec.dat b/venv/lib/python3.12/site-packages/babel/locale-data/vec.dat new file mode 100644 index 00000000..59dcdec1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/vec.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/vec_IT.dat b/venv/lib/python3.12/site-packages/babel/locale-data/vec_IT.dat new file mode 100644 index 00000000..a1f0238c Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/vec_IT.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/vi.dat b/venv/lib/python3.12/site-packages/babel/locale-data/vi.dat new file mode 100644 index 00000000..e887dca5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/vi.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/vi_VN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/vi_VN.dat new file mode 100644 index 00000000..d1942c8e Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/vi_VN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/vmw.dat b/venv/lib/python3.12/site-packages/babel/locale-data/vmw.dat new file mode 100644 index 00000000..7116de27 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/vmw.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/vmw_MZ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/vmw_MZ.dat new file mode 100644 index 00000000..21999745 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/vmw_MZ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/vo.dat b/venv/lib/python3.12/site-packages/babel/locale-data/vo.dat new file mode 100644 index 00000000..00137183 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/vo.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/vo_001.dat b/venv/lib/python3.12/site-packages/babel/locale-data/vo_001.dat new file mode 100644 index 00000000..e06ed1c8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/vo_001.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/vun.dat b/venv/lib/python3.12/site-packages/babel/locale-data/vun.dat new file mode 100644 index 00000000..8fd175bb Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/vun.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/vun_TZ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/vun_TZ.dat new file mode 100644 index 00000000..a29794a6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/vun_TZ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/wa.dat b/venv/lib/python3.12/site-packages/babel/locale-data/wa.dat new file mode 100644 index 00000000..6699fd21 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/wa.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/wa_BE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/wa_BE.dat new file mode 100644 index 00000000..63f30c15 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/wa_BE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/wae.dat b/venv/lib/python3.12/site-packages/babel/locale-data/wae.dat new file mode 100644 index 00000000..7deede2e Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/wae.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/wae_CH.dat b/venv/lib/python3.12/site-packages/babel/locale-data/wae_CH.dat new file mode 100644 index 00000000..7e10feea Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/wae_CH.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/wal.dat b/venv/lib/python3.12/site-packages/babel/locale-data/wal.dat new file mode 100644 index 00000000..99770a06 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/wal.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/wal_ET.dat b/venv/lib/python3.12/site-packages/babel/locale-data/wal_ET.dat new file mode 100644 index 00000000..fa5fc6d0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/wal_ET.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/wbp.dat b/venv/lib/python3.12/site-packages/babel/locale-data/wbp.dat new file mode 100644 index 00000000..22bcdd77 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/wbp.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/wbp_AU.dat b/venv/lib/python3.12/site-packages/babel/locale-data/wbp_AU.dat new file mode 100644 index 00000000..3b6b5b1e Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/wbp_AU.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/wo.dat b/venv/lib/python3.12/site-packages/babel/locale-data/wo.dat new file mode 100644 index 00000000..ed881e93 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/wo.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/wo_SN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/wo_SN.dat new file mode 100644 index 00000000..ce673ad4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/wo_SN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/xh.dat b/venv/lib/python3.12/site-packages/babel/locale-data/xh.dat new file mode 100644 index 00000000..487e18a0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/xh.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/xh_ZA.dat b/venv/lib/python3.12/site-packages/babel/locale-data/xh_ZA.dat new file mode 100644 index 00000000..68d71668 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/xh_ZA.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/xnr.dat b/venv/lib/python3.12/site-packages/babel/locale-data/xnr.dat new file mode 100644 index 00000000..3b0118bd Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/xnr.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/xnr_IN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/xnr_IN.dat new file mode 100644 index 00000000..360f58c4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/xnr_IN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/xog.dat b/venv/lib/python3.12/site-packages/babel/locale-data/xog.dat new file mode 100644 index 00000000..365dd8aa Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/xog.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/xog_UG.dat b/venv/lib/python3.12/site-packages/babel/locale-data/xog_UG.dat new file mode 100644 index 00000000..b5fb4da0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/xog_UG.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/yav.dat b/venv/lib/python3.12/site-packages/babel/locale-data/yav.dat new file mode 100644 index 00000000..547b6b91 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/yav.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/yav_CM.dat b/venv/lib/python3.12/site-packages/babel/locale-data/yav_CM.dat new file mode 100644 index 00000000..2a0cbb26 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/yav_CM.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/yi.dat b/venv/lib/python3.12/site-packages/babel/locale-data/yi.dat new file mode 100644 index 00000000..c39639f0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/yi.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/yi_UA.dat b/venv/lib/python3.12/site-packages/babel/locale-data/yi_UA.dat new file mode 100644 index 00000000..9f967db0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/yi_UA.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/yo.dat b/venv/lib/python3.12/site-packages/babel/locale-data/yo.dat new file mode 100644 index 00000000..a59c6ef9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/yo.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/yo_BJ.dat b/venv/lib/python3.12/site-packages/babel/locale-data/yo_BJ.dat new file mode 100644 index 00000000..b4239959 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/yo_BJ.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/yo_NG.dat b/venv/lib/python3.12/site-packages/babel/locale-data/yo_NG.dat new file mode 100644 index 00000000..e473a96c Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/yo_NG.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/yrl.dat b/venv/lib/python3.12/site-packages/babel/locale-data/yrl.dat new file mode 100644 index 00000000..946ffccf Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/yrl.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/yrl_BR.dat b/venv/lib/python3.12/site-packages/babel/locale-data/yrl_BR.dat new file mode 100644 index 00000000..bf1012ee Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/yrl_BR.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/yrl_CO.dat b/venv/lib/python3.12/site-packages/babel/locale-data/yrl_CO.dat new file mode 100644 index 00000000..c8a8c22e Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/yrl_CO.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/yrl_VE.dat b/venv/lib/python3.12/site-packages/babel/locale-data/yrl_VE.dat new file mode 100644 index 00000000..d8ebd8ed Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/yrl_VE.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/yue.dat b/venv/lib/python3.12/site-packages/babel/locale-data/yue.dat new file mode 100644 index 00000000..e2fbe58b Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/yue.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/yue_Hans.dat b/venv/lib/python3.12/site-packages/babel/locale-data/yue_Hans.dat new file mode 100644 index 00000000..73446b87 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/yue_Hans.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/yue_Hans_CN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/yue_Hans_CN.dat new file mode 100644 index 00000000..1ba61971 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/yue_Hans_CN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/yue_Hant.dat b/venv/lib/python3.12/site-packages/babel/locale-data/yue_Hant.dat new file mode 100644 index 00000000..d6740346 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/yue_Hant.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/yue_Hant_CN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/yue_Hant_CN.dat new file mode 100644 index 00000000..1ba61971 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/yue_Hant_CN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/yue_Hant_HK.dat b/venv/lib/python3.12/site-packages/babel/locale-data/yue_Hant_HK.dat new file mode 100644 index 00000000..853efcbb Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/yue_Hant_HK.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/za.dat b/venv/lib/python3.12/site-packages/babel/locale-data/za.dat new file mode 100644 index 00000000..174e6839 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/za.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/za_CN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/za_CN.dat new file mode 100644 index 00000000..f0cbb5db Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/za_CN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/zgh.dat b/venv/lib/python3.12/site-packages/babel/locale-data/zgh.dat new file mode 100644 index 00000000..a0c342c2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/zgh.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/zgh_MA.dat b/venv/lib/python3.12/site-packages/babel/locale-data/zgh_MA.dat new file mode 100644 index 00000000..8475afed Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/zgh_MA.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/zh.dat b/venv/lib/python3.12/site-packages/babel/locale-data/zh.dat new file mode 100644 index 00000000..ddf76651 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/zh.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/zh_Hans.dat b/venv/lib/python3.12/site-packages/babel/locale-data/zh_Hans.dat new file mode 100644 index 00000000..c5c94bf8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/zh_Hans.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/zh_Hans_CN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/zh_Hans_CN.dat new file mode 100644 index 00000000..09d08c10 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/zh_Hans_CN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/zh_Hans_HK.dat b/venv/lib/python3.12/site-packages/babel/locale-data/zh_Hans_HK.dat new file mode 100644 index 00000000..2396da0a Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/zh_Hans_HK.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/zh_Hans_MO.dat b/venv/lib/python3.12/site-packages/babel/locale-data/zh_Hans_MO.dat new file mode 100644 index 00000000..4f0232e5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/zh_Hans_MO.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/zh_Hans_MY.dat b/venv/lib/python3.12/site-packages/babel/locale-data/zh_Hans_MY.dat new file mode 100644 index 00000000..bddaab92 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/zh_Hans_MY.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/zh_Hans_SG.dat b/venv/lib/python3.12/site-packages/babel/locale-data/zh_Hans_SG.dat new file mode 100644 index 00000000..646b57d6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/zh_Hans_SG.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/zh_Hant.dat b/venv/lib/python3.12/site-packages/babel/locale-data/zh_Hant.dat new file mode 100644 index 00000000..fbfcef27 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/zh_Hant.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/zh_Hant_HK.dat b/venv/lib/python3.12/site-packages/babel/locale-data/zh_Hant_HK.dat new file mode 100644 index 00000000..d8ef238f Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/zh_Hant_HK.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/zh_Hant_MO.dat b/venv/lib/python3.12/site-packages/babel/locale-data/zh_Hant_MO.dat new file mode 100644 index 00000000..2e468357 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/zh_Hant_MO.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/zh_Hant_MY.dat b/venv/lib/python3.12/site-packages/babel/locale-data/zh_Hant_MY.dat new file mode 100644 index 00000000..1198fb41 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/zh_Hant_MY.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/zh_Hant_TW.dat b/venv/lib/python3.12/site-packages/babel/locale-data/zh_Hant_TW.dat new file mode 100644 index 00000000..657e8885 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/zh_Hant_TW.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/zh_Latn.dat b/venv/lib/python3.12/site-packages/babel/locale-data/zh_Latn.dat new file mode 100644 index 00000000..c5c94bf8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/zh_Latn.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/zh_Latn_CN.dat b/venv/lib/python3.12/site-packages/babel/locale-data/zh_Latn_CN.dat new file mode 100644 index 00000000..09d08c10 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/zh_Latn_CN.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/zu.dat b/venv/lib/python3.12/site-packages/babel/locale-data/zu.dat new file mode 100644 index 00000000..8c98018b Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/zu.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/locale-data/zu_ZA.dat b/venv/lib/python3.12/site-packages/babel/locale-data/zu_ZA.dat new file mode 100644 index 00000000..026694ca Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/locale-data/zu_ZA.dat differ diff --git a/venv/lib/python3.12/site-packages/babel/localedata.py b/venv/lib/python3.12/site-packages/babel/localedata.py new file mode 100644 index 00000000..59f1db09 --- /dev/null +++ b/venv/lib/python3.12/site-packages/babel/localedata.py @@ -0,0 +1,278 @@ +""" + babel.localedata + ~~~~~~~~~~~~~~~~ + + Low-level locale data access. + + :note: The `Locale` class, which uses this module under the hood, provides a + more convenient interface for accessing the locale data. + + :copyright: (c) 2013-2025 by the Babel Team. + :license: BSD, see LICENSE for more details. +""" + +from __future__ import annotations + +import os +import pickle +import re +import sys +import threading +from collections import abc +from collections.abc import Iterator, Mapping, MutableMapping +from functools import lru_cache +from itertools import chain +from typing import Any + +_cache: dict[str, Any] = {} +_cache_lock = threading.RLock() +_dirname = os.path.join(os.path.dirname(__file__), 'locale-data') +_windows_reserved_name_re = re.compile("^(con|prn|aux|nul|com[0-9]|lpt[0-9])$", re.I) + + +def normalize_locale(name: str) -> str | None: + """Normalize a locale ID by stripping spaces and apply proper casing. + + Returns the normalized locale ID string or `None` if the ID is not + recognized. + """ + if not name or not isinstance(name, str): + return None + name = name.strip().lower() + for locale_id in chain.from_iterable([_cache, locale_identifiers()]): + if name == locale_id.lower(): + return locale_id + + +def resolve_locale_filename(name: os.PathLike[str] | str) -> str: + """ + Resolve a locale identifier to a `.dat` path on disk. + """ + + # Clean up any possible relative paths. + name = os.path.basename(name) + + # Ensure we're not left with one of the Windows reserved names. + if sys.platform == "win32" and _windows_reserved_name_re.match(os.path.splitext(name)[0]): + raise ValueError(f"Name {name} is invalid on Windows") + + # Build the path. + return os.path.join(_dirname, f"{name}.dat") + + +def exists(name: str) -> bool: + """Check whether locale data is available for the given locale. + + Returns `True` if it exists, `False` otherwise. + + :param name: the locale identifier string + """ + if not name or not isinstance(name, str): + return False + if name in _cache: + return True + file_found = os.path.exists(resolve_locale_filename(name)) + return True if file_found else bool(normalize_locale(name)) + + +@lru_cache(maxsize=None) +def locale_identifiers() -> list[str]: + """Return a list of all locale identifiers for which locale data is + available. + + This data is cached after the first invocation. + You can clear the cache by calling `locale_identifiers.cache_clear()`. + + .. versionadded:: 0.8.1 + + :return: a list of locale identifiers (strings) + """ + return [ + stem + for stem, extension in + (os.path.splitext(filename) for filename in os.listdir(_dirname)) + if extension == '.dat' and stem != 'root' + ] + + +def _is_non_likely_script(name: str) -> bool: + """Return whether the locale is of the form ``lang_Script``, + and the script is not the likely script for the language. + + This implements the behavior of the ``nonlikelyScript`` value of the + ``localRules`` attribute for parent locales added in CLDR 45. + """ + from babel.core import get_global, parse_locale + + try: + lang, territory, script, variant, *rest = parse_locale(name) + except ValueError: + return False + + if lang and script and not territory and not variant and not rest: + likely_subtag = get_global('likely_subtags').get(lang) + _, _, likely_script, *_ = parse_locale(likely_subtag) + return script != likely_script + return False + + +def load(name: os.PathLike[str] | str, merge_inherited: bool = True) -> dict[str, Any]: + """Load the locale data for the given locale. + + The locale data is a dictionary that contains much of the data defined by + the Common Locale Data Repository (CLDR). This data is stored as a + collection of pickle files inside the ``babel`` package. + + >>> d = load('en_US') + >>> d['languages']['sv'] + u'Swedish' + + Note that the results are cached, and subsequent requests for the same + locale return the same dictionary: + + >>> d1 = load('en_US') + >>> d2 = load('en_US') + >>> d1 is d2 + True + + :param name: the locale identifier string (or "root") + :param merge_inherited: whether the inherited data should be merged into + the data of the requested locale + :raise `IOError`: if no locale data file is found for the given locale + identifier, or one of the locales it inherits from + """ + name = os.path.basename(name) + _cache_lock.acquire() + try: + data = _cache.get(name) + if not data: + # Load inherited data + if name == 'root' or not merge_inherited: + data = {} + else: + from babel.core import get_global + parent = get_global('parent_exceptions').get(name) + if not parent: + if _is_non_likely_script(name): + parent = 'root' + else: + parts = name.split('_') + parent = "root" if len(parts) == 1 else "_".join(parts[:-1]) + data = load(parent).copy() + filename = resolve_locale_filename(name) + with open(filename, 'rb') as fileobj: + if name != 'root' and merge_inherited: + merge(data, pickle.load(fileobj)) + else: + data = pickle.load(fileobj) + _cache[name] = data + return data + finally: + _cache_lock.release() + + +def merge(dict1: MutableMapping[Any, Any], dict2: Mapping[Any, Any]) -> None: + """Merge the data from `dict2` into the `dict1` dictionary, making copies + of nested dictionaries. + + >>> d = {1: 'foo', 3: 'baz'} + >>> merge(d, {1: 'Foo', 2: 'Bar'}) + >>> sorted(d.items()) + [(1, 'Foo'), (2, 'Bar'), (3, 'baz')] + + :param dict1: the dictionary to merge into + :param dict2: the dictionary containing the data that should be merged + """ + for key, val2 in dict2.items(): + if val2 is not None: + val1 = dict1.get(key) + if isinstance(val2, dict): + if val1 is None: + val1 = {} + if isinstance(val1, Alias): + val1 = (val1, val2) + elif isinstance(val1, tuple): + alias, others = val1 + others = others.copy() + merge(others, val2) + val1 = (alias, others) + else: + val1 = val1.copy() + merge(val1, val2) + else: + val1 = val2 + dict1[key] = val1 + + +class Alias: + """Representation of an alias in the locale data. + + An alias is a value that refers to some other part of the locale data, + as specified by the `keys`. + """ + + def __init__(self, keys: tuple[str, ...]) -> None: + self.keys = tuple(keys) + + def __repr__(self) -> str: + return f"<{type(self).__name__} {self.keys!r}>" + + def resolve(self, data: Mapping[str | int | None, Any]) -> Mapping[str | int | None, Any]: + """Resolve the alias based on the given data. + + This is done recursively, so if one alias resolves to a second alias, + that second alias will also be resolved. + + :param data: the locale data + :type data: `dict` + """ + base = data + for key in self.keys: + data = data[key] + if isinstance(data, Alias): + data = data.resolve(base) + elif isinstance(data, tuple): + alias, others = data + data = alias.resolve(base) + return data + + +class LocaleDataDict(abc.MutableMapping): + """Dictionary wrapper that automatically resolves aliases to the actual + values. + """ + + def __init__(self, data: MutableMapping[str | int | None, Any], base: Mapping[str | int | None, Any] | None = None): + self._data = data + if base is None: + base = data + self.base = base + + def __len__(self) -> int: + return len(self._data) + + def __iter__(self) -> Iterator[str | int | None]: + return iter(self._data) + + def __getitem__(self, key: str | int | None) -> Any: + orig = val = self._data[key] + if isinstance(val, Alias): # resolve an alias + val = val.resolve(self.base) + if isinstance(val, tuple): # Merge a partial dict with an alias + alias, others = val + val = alias.resolve(self.base).copy() + merge(val, others) + if isinstance(val, dict): # Return a nested alias-resolving dict + val = LocaleDataDict(val, base=self.base) + if val is not orig: + self._data[key] = val + return val + + def __setitem__(self, key: str | int | None, value: Any) -> None: + self._data[key] = value + + def __delitem__(self, key: str | int | None) -> None: + del self._data[key] + + def copy(self) -> LocaleDataDict: + return LocaleDataDict(self._data.copy(), base=self.base) diff --git a/venv/lib/python3.12/site-packages/babel/localtime/__init__.py b/venv/lib/python3.12/site-packages/babel/localtime/__init__.py new file mode 100644 index 00000000..854c0749 --- /dev/null +++ b/venv/lib/python3.12/site-packages/babel/localtime/__init__.py @@ -0,0 +1,43 @@ +""" + babel.localtime + ~~~~~~~~~~~~~~~ + + Babel specific fork of tzlocal to determine the local timezone + of the system. + + :copyright: (c) 2013-2025 by the Babel Team. + :license: BSD, see LICENSE for more details. +""" + +import datetime +import sys + +if sys.platform == 'win32': + from babel.localtime._win32 import _get_localzone +else: + from babel.localtime._unix import _get_localzone + + +# TODO(3.0): the offset constants are not part of the public API +# and should be removed +from babel.localtime._fallback import ( + DSTDIFF, # noqa: F401 + DSTOFFSET, # noqa: F401 + STDOFFSET, # noqa: F401 + ZERO, # noqa: F401 + _FallbackLocalTimezone, +) + + +def get_localzone() -> datetime.tzinfo: + """Returns the current underlying local timezone object. + Generally this function does not need to be used, it's a + better idea to use the :data:`LOCALTZ` singleton instead. + """ + return _get_localzone() + + +try: + LOCALTZ = get_localzone() +except LookupError: + LOCALTZ = _FallbackLocalTimezone() diff --git a/venv/lib/python3.12/site-packages/babel/localtime/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/babel/localtime/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 00000000..c3fa0432 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/localtime/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/babel/localtime/__pycache__/_fallback.cpython-312.pyc b/venv/lib/python3.12/site-packages/babel/localtime/__pycache__/_fallback.cpython-312.pyc new file mode 100644 index 00000000..0e604a3b Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/localtime/__pycache__/_fallback.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/babel/localtime/__pycache__/_helpers.cpython-312.pyc b/venv/lib/python3.12/site-packages/babel/localtime/__pycache__/_helpers.cpython-312.pyc new file mode 100644 index 00000000..e757994a Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/localtime/__pycache__/_helpers.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/babel/localtime/__pycache__/_unix.cpython-312.pyc b/venv/lib/python3.12/site-packages/babel/localtime/__pycache__/_unix.cpython-312.pyc new file mode 100644 index 00000000..1bbaa105 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/localtime/__pycache__/_unix.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/babel/localtime/__pycache__/_win32.cpython-312.pyc b/venv/lib/python3.12/site-packages/babel/localtime/__pycache__/_win32.cpython-312.pyc new file mode 100644 index 00000000..29acccbf Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/localtime/__pycache__/_win32.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/babel/localtime/_fallback.py b/venv/lib/python3.12/site-packages/babel/localtime/_fallback.py new file mode 100644 index 00000000..fab6867c --- /dev/null +++ b/venv/lib/python3.12/site-packages/babel/localtime/_fallback.py @@ -0,0 +1,44 @@ +""" + babel.localtime._fallback + ~~~~~~~~~~~~~~~~~~~~~~~~~ + + Emulated fallback local timezone when all else fails. + + :copyright: (c) 2013-2025 by the Babel Team. + :license: BSD, see LICENSE for more details. +""" + +import datetime +import time + +STDOFFSET = datetime.timedelta(seconds=-time.timezone) +DSTOFFSET = datetime.timedelta(seconds=-time.altzone) if time.daylight else STDOFFSET + +DSTDIFF = DSTOFFSET - STDOFFSET +ZERO = datetime.timedelta(0) + + +class _FallbackLocalTimezone(datetime.tzinfo): + + def utcoffset(self, dt: datetime.datetime) -> datetime.timedelta: + if self._isdst(dt): + return DSTOFFSET + else: + return STDOFFSET + + def dst(self, dt: datetime.datetime) -> datetime.timedelta: + if self._isdst(dt): + return DSTDIFF + else: + return ZERO + + def tzname(self, dt: datetime.datetime) -> str: + return time.tzname[self._isdst(dt)] + + def _isdst(self, dt: datetime.datetime) -> bool: + tt = (dt.year, dt.month, dt.day, + dt.hour, dt.minute, dt.second, + dt.weekday(), 0, -1) + stamp = time.mktime(tt) + tt = time.localtime(stamp) + return tt.tm_isdst > 0 diff --git a/venv/lib/python3.12/site-packages/babel/localtime/_helpers.py b/venv/lib/python3.12/site-packages/babel/localtime/_helpers.py new file mode 100644 index 00000000..e7e67052 --- /dev/null +++ b/venv/lib/python3.12/site-packages/babel/localtime/_helpers.py @@ -0,0 +1,57 @@ +try: + import pytz +except ModuleNotFoundError: + pytz = None + +try: + import zoneinfo +except ModuleNotFoundError: + zoneinfo = None + + +def _get_tzinfo(tzenv: str): + """Get the tzinfo from `zoneinfo` or `pytz` + + :param tzenv: timezone in the form of Continent/City + :return: tzinfo object or None if not found + """ + if pytz: + try: + return pytz.timezone(tzenv) + except pytz.UnknownTimeZoneError: + pass + else: + try: + return zoneinfo.ZoneInfo(tzenv) + except ValueError as ve: + # This is somewhat hacky, but since _validate_tzfile_path() doesn't + # raise a specific error type, we'll need to check the message to be + # one we know to be from that function. + # If so, we pretend it meant that the TZ didn't exist, for the benefit + # of `babel.localtime` catching the `LookupError` raised by + # `_get_tzinfo_or_raise()`. + # See https://github.com/python-babel/babel/issues/1092 + if str(ve).startswith("ZoneInfo keys "): + return None + except zoneinfo.ZoneInfoNotFoundError: + pass + + return None + + +def _get_tzinfo_or_raise(tzenv: str): + tzinfo = _get_tzinfo(tzenv) + if tzinfo is None: + raise LookupError( + f"Can not find timezone {tzenv}. \n" + "Timezone names are generally in the form `Continent/City`.", + ) + return tzinfo + + +def _get_tzinfo_from_file(tzfilename: str): + with open(tzfilename, 'rb') as tzfile: + if pytz: + return pytz.tzfile.build_tzinfo('local', tzfile) + else: + return zoneinfo.ZoneInfo.from_file(tzfile) diff --git a/venv/lib/python3.12/site-packages/babel/localtime/_unix.py b/venv/lib/python3.12/site-packages/babel/localtime/_unix.py new file mode 100644 index 00000000..782a7d24 --- /dev/null +++ b/venv/lib/python3.12/site-packages/babel/localtime/_unix.py @@ -0,0 +1,104 @@ +import datetime +import os +import re + +from babel.localtime._helpers import ( + _get_tzinfo, + _get_tzinfo_from_file, + _get_tzinfo_or_raise, +) + + +def _tz_from_env(tzenv: str) -> datetime.tzinfo: + if tzenv[0] == ':': + tzenv = tzenv[1:] + + # TZ specifies a file + if os.path.exists(tzenv): + return _get_tzinfo_from_file(tzenv) + + # TZ specifies a zoneinfo zone. + return _get_tzinfo_or_raise(tzenv) + + +def _get_localzone(_root: str = '/') -> datetime.tzinfo: + """Tries to find the local timezone configuration. + This method prefers finding the timezone name and passing that to + zoneinfo or pytz, over passing in the localtime file, as in the later + case the zoneinfo name is unknown. + The parameter _root makes the function look for files like /etc/localtime + beneath the _root directory. This is primarily used by the tests. + In normal usage you call the function without parameters. + """ + + tzenv = os.environ.get('TZ') + if tzenv: + return _tz_from_env(tzenv) + + # This is actually a pretty reliable way to test for the local time + # zone on operating systems like OS X. On OS X especially this is the + # only one that actually works. + try: + link_dst = os.readlink('/etc/localtime') + except OSError: + pass + else: + pos = link_dst.find('/zoneinfo/') + if pos >= 0: + # On occasion, the `/etc/localtime` symlink has a double slash, e.g. + # "/usr/share/zoneinfo//UTC", which would make `zoneinfo.ZoneInfo` + # complain (no absolute paths allowed), and we'd end up returning + # `None` (as a fix for #1092). + # Instead, let's just "fix" the double slash symlink by stripping + # leading slashes before passing the assumed zone name forward. + zone_name = link_dst[pos + 10:].lstrip("/") + tzinfo = _get_tzinfo(zone_name) + if tzinfo is not None: + return tzinfo + + # Now look for distribution specific configuration files + # that contain the timezone name. + tzpath = os.path.join(_root, 'etc/timezone') + if os.path.exists(tzpath): + with open(tzpath, 'rb') as tzfile: + data = tzfile.read() + + # Issue #3 in tzlocal was that /etc/timezone was a zoneinfo file. + # That's a misconfiguration, but we need to handle it gracefully: + if data[:5] != b'TZif2': + etctz = data.strip().decode() + # Get rid of host definitions and comments: + if ' ' in etctz: + etctz, dummy = etctz.split(' ', 1) + if '#' in etctz: + etctz, dummy = etctz.split('#', 1) + + return _get_tzinfo_or_raise(etctz.replace(' ', '_')) + + # CentOS has a ZONE setting in /etc/sysconfig/clock, + # OpenSUSE has a TIMEZONE setting in /etc/sysconfig/clock and + # Gentoo has a TIMEZONE setting in /etc/conf.d/clock + # We look through these files for a timezone: + timezone_re = re.compile(r'\s*(TIME)?ZONE\s*=\s*"(?P.+)"') + + for filename in ('etc/sysconfig/clock', 'etc/conf.d/clock'): + tzpath = os.path.join(_root, filename) + if not os.path.exists(tzpath): + continue + with open(tzpath) as tzfile: + for line in tzfile: + match = timezone_re.match(line) + if match is not None: + # We found a timezone + etctz = match.group("etctz") + return _get_tzinfo_or_raise(etctz.replace(' ', '_')) + + # No explicit setting existed. Use localtime + for filename in ('etc/localtime', 'usr/local/etc/localtime'): + tzpath = os.path.join(_root, filename) + + if not os.path.exists(tzpath): + continue + return _get_tzinfo_from_file(tzpath) + + raise LookupError('Can not find any timezone configuration') diff --git a/venv/lib/python3.12/site-packages/babel/localtime/_win32.py b/venv/lib/python3.12/site-packages/babel/localtime/_win32.py new file mode 100644 index 00000000..1a52567b --- /dev/null +++ b/venv/lib/python3.12/site-packages/babel/localtime/_win32.py @@ -0,0 +1,98 @@ +from __future__ import annotations + +try: + import winreg +except ImportError: + winreg = None + +import datetime +from typing import Any, Dict, cast + +from babel.core import get_global +from babel.localtime._helpers import _get_tzinfo_or_raise + +# When building the cldr data on windows this module gets imported. +# Because at that point there is no global.dat yet this call will +# fail. We want to catch it down in that case then and just assume +# the mapping was empty. +try: + tz_names: dict[str, str] = cast(Dict[str, str], get_global('windows_zone_mapping')) +except RuntimeError: + tz_names = {} + + +def valuestodict(key) -> dict[str, Any]: + """Convert a registry key's values to a dictionary.""" + dict = {} + size = winreg.QueryInfoKey(key)[1] + for i in range(size): + data = winreg.EnumValue(key, i) + dict[data[0]] = data[1] + return dict + + +def get_localzone_name() -> str: + # Windows is special. It has unique time zone names (in several + # meanings of the word) available, but unfortunately, they can be + # translated to the language of the operating system, so we need to + # do a backwards lookup, by going through all time zones and see which + # one matches. + handle = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) + + TZLOCALKEYNAME = r'SYSTEM\CurrentControlSet\Control\TimeZoneInformation' + localtz = winreg.OpenKey(handle, TZLOCALKEYNAME) + keyvalues = valuestodict(localtz) + localtz.Close() + if 'TimeZoneKeyName' in keyvalues: + # Windows 7 (and Vista?) + + # For some reason this returns a string with loads of NUL bytes at + # least on some systems. I don't know if this is a bug somewhere, I + # just work around it. + tzkeyname = keyvalues['TimeZoneKeyName'].split('\x00', 1)[0] + else: + # Windows 2000 or XP + + # This is the localized name: + tzwin = keyvalues['StandardName'] + + # Open the list of timezones to look up the real name: + TZKEYNAME = r'SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones' + tzkey = winreg.OpenKey(handle, TZKEYNAME) + + # Now, match this value to Time Zone information + tzkeyname = None + for i in range(winreg.QueryInfoKey(tzkey)[0]): + subkey = winreg.EnumKey(tzkey, i) + sub = winreg.OpenKey(tzkey, subkey) + data = valuestodict(sub) + sub.Close() + if data.get('Std', None) == tzwin: + tzkeyname = subkey + break + + tzkey.Close() + handle.Close() + + if tzkeyname is None: + raise LookupError('Can not find Windows timezone configuration') + + timezone = tz_names.get(tzkeyname) + if timezone is None: + # Nope, that didn't work. Try adding 'Standard Time', + # it seems to work a lot of times: + timezone = tz_names.get(f"{tzkeyname} Standard Time") + + # Return what we have. + if timezone is None: + raise LookupError(f"Can not find timezone {tzkeyname}") + + return timezone + + +def _get_localzone() -> datetime.tzinfo: + if winreg is None: + raise LookupError( + 'Runtime support not available') + + return _get_tzinfo_or_raise(get_localzone_name()) diff --git a/venv/lib/python3.12/site-packages/babel/messages/__init__.py b/venv/lib/python3.12/site-packages/babel/messages/__init__.py new file mode 100644 index 00000000..ca83faa9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/babel/messages/__init__.py @@ -0,0 +1,21 @@ +""" + babel.messages + ~~~~~~~~~~~~~~ + + Support for ``gettext`` message catalogs. + + :copyright: (c) 2013-2025 by the Babel Team. + :license: BSD, see LICENSE for more details. +""" + +from babel.messages.catalog import ( + Catalog, + Message, + TranslationError, +) + +__all__ = [ + "Catalog", + "Message", + "TranslationError", +] diff --git a/venv/lib/python3.12/site-packages/babel/messages/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/babel/messages/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 00000000..683a32bb Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/messages/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/babel/messages/__pycache__/_compat.cpython-312.pyc b/venv/lib/python3.12/site-packages/babel/messages/__pycache__/_compat.cpython-312.pyc new file mode 100644 index 00000000..22785e57 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/messages/__pycache__/_compat.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/babel/messages/__pycache__/catalog.cpython-312.pyc b/venv/lib/python3.12/site-packages/babel/messages/__pycache__/catalog.cpython-312.pyc new file mode 100644 index 00000000..d18c5216 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/messages/__pycache__/catalog.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/babel/messages/__pycache__/checkers.cpython-312.pyc b/venv/lib/python3.12/site-packages/babel/messages/__pycache__/checkers.cpython-312.pyc new file mode 100644 index 00000000..33378000 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/messages/__pycache__/checkers.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/babel/messages/__pycache__/extract.cpython-312.pyc b/venv/lib/python3.12/site-packages/babel/messages/__pycache__/extract.cpython-312.pyc new file mode 100644 index 00000000..5e365848 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/messages/__pycache__/extract.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/babel/messages/__pycache__/frontend.cpython-312.pyc b/venv/lib/python3.12/site-packages/babel/messages/__pycache__/frontend.cpython-312.pyc new file mode 100644 index 00000000..3d269918 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/messages/__pycache__/frontend.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/babel/messages/__pycache__/jslexer.cpython-312.pyc b/venv/lib/python3.12/site-packages/babel/messages/__pycache__/jslexer.cpython-312.pyc new file mode 100644 index 00000000..1e38bdf6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/messages/__pycache__/jslexer.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/babel/messages/__pycache__/mofile.cpython-312.pyc b/venv/lib/python3.12/site-packages/babel/messages/__pycache__/mofile.cpython-312.pyc new file mode 100644 index 00000000..e2d4216a Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/messages/__pycache__/mofile.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/babel/messages/__pycache__/plurals.cpython-312.pyc b/venv/lib/python3.12/site-packages/babel/messages/__pycache__/plurals.cpython-312.pyc new file mode 100644 index 00000000..346e4738 Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/messages/__pycache__/plurals.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/babel/messages/__pycache__/pofile.cpython-312.pyc b/venv/lib/python3.12/site-packages/babel/messages/__pycache__/pofile.cpython-312.pyc new file mode 100644 index 00000000..e6c4ab0b Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/messages/__pycache__/pofile.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/babel/messages/__pycache__/setuptools_frontend.cpython-312.pyc b/venv/lib/python3.12/site-packages/babel/messages/__pycache__/setuptools_frontend.cpython-312.pyc new file mode 100644 index 00000000..27ad4f8d Binary files /dev/null and b/venv/lib/python3.12/site-packages/babel/messages/__pycache__/setuptools_frontend.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/babel/messages/_compat.py b/venv/lib/python3.12/site-packages/babel/messages/_compat.py new file mode 100644 index 00000000..319b545f --- /dev/null +++ b/venv/lib/python3.12/site-packages/babel/messages/_compat.py @@ -0,0 +1,34 @@ +import sys +from functools import partial + + +def find_entrypoints(group_name: str): + """ + Find entrypoints of a given group using either `importlib.metadata` or the + older `pkg_resources` mechanism. + + Yields tuples of the entrypoint name and a callable function that will + load the actual entrypoint. + """ + if sys.version_info >= (3, 10): + # "Changed in version 3.10: importlib.metadata is no longer provisional." + try: + from importlib.metadata import entry_points + except ImportError: + pass + else: + eps = entry_points(group=group_name) + # Only do this if this implementation of `importlib.metadata` is + # modern enough to not return a dict. + if not isinstance(eps, dict): + for entry_point in eps: + yield (entry_point.name, entry_point.load) + return + + try: + from pkg_resources import working_set + except ImportError: + pass + else: + for entry_point in working_set.iter_entry_points(group_name): + yield (entry_point.name, partial(entry_point.load, require=True)) diff --git a/venv/lib/python3.12/site-packages/babel/messages/catalog.py b/venv/lib/python3.12/site-packages/babel/messages/catalog.py new file mode 100644 index 00000000..f84a5bd1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/babel/messages/catalog.py @@ -0,0 +1,1000 @@ +""" + babel.messages.catalog + ~~~~~~~~~~~~~~~~~~~~~~ + + Data structures for message catalogs. + + :copyright: (c) 2013-2025 by the Babel Team. + :license: BSD, see LICENSE for more details. +""" +from __future__ import annotations + +import datetime +import re +from collections.abc import Iterable, Iterator +from copy import copy +from difflib import SequenceMatcher +from email import message_from_string +from heapq import nlargest +from string import Formatter +from typing import TYPE_CHECKING + +from babel import __version__ as VERSION +from babel.core import Locale, UnknownLocaleError +from babel.dates import format_datetime +from babel.messages.plurals import get_plural +from babel.util import LOCALTZ, FixedOffsetTimezone, _cmp, distinct + +if TYPE_CHECKING: + from typing_extensions import TypeAlias + + _MessageID: TypeAlias = str | tuple[str, ...] | list[str] + +__all__ = [ + 'DEFAULT_HEADER', + 'PYTHON_FORMAT', + 'Catalog', + 'Message', + 'TranslationError', +] + + +def get_close_matches(word, possibilities, n=3, cutoff=0.6): + """A modified version of ``difflib.get_close_matches``. + + It just passes ``autojunk=False`` to the ``SequenceMatcher``, to work + around https://github.com/python/cpython/issues/90825. + """ + if not n > 0: # pragma: no cover + raise ValueError(f"n must be > 0: {n!r}") + if not 0.0 <= cutoff <= 1.0: # pragma: no cover + raise ValueError(f"cutoff must be in [0.0, 1.0]: {cutoff!r}") + result = [] + s = SequenceMatcher(autojunk=False) # only line changed from difflib.py + s.set_seq2(word) + for x in possibilities: + s.set_seq1(x) + if s.real_quick_ratio() >= cutoff and \ + s.quick_ratio() >= cutoff and \ + s.ratio() >= cutoff: + result.append((s.ratio(), x)) + + # Move the best scorers to head of list + result = nlargest(n, result) + # Strip scores for the best n matches + return [x for score, x in result] + + +PYTHON_FORMAT = re.compile(r''' + \% + (?:\(([\w]*)\))? + ( + [-#0\ +]?(?:\*|[\d]+)? + (?:\.(?:\*|[\d]+))? + [hlL]? + ) + ([diouxXeEfFgGcrs%]) +''', re.VERBOSE) + + +def _has_python_brace_format(string: str) -> bool: + if "{" not in string: + return False + fmt = Formatter() + try: + # `fmt.parse` returns 3-or-4-tuples of the form + # `(literal_text, field_name, format_spec, conversion)`; + # if `field_name` is set, this smells like brace format + field_name_seen = False + for t in fmt.parse(string): + if t[1] is not None: + field_name_seen = True + # We cannot break here, as we need to consume the whole string + # to ensure that it is a valid format string. + except ValueError: + return False + return field_name_seen + + +def _parse_datetime_header(value: str) -> datetime.datetime: + match = re.match(r'^(?P.*?)(?P[+-]\d{4})?$', value) + + dt = datetime.datetime.strptime(match.group('datetime'), '%Y-%m-%d %H:%M') + + # Separate the offset into a sign component, hours, and # minutes + tzoffset = match.group('tzoffset') + if tzoffset is not None: + plus_minus_s, rest = tzoffset[0], tzoffset[1:] + hours_offset_s, mins_offset_s = rest[:2], rest[2:] + + # Make them all integers + plus_minus = int(f"{plus_minus_s}1") + hours_offset = int(hours_offset_s) + mins_offset = int(mins_offset_s) + + # Calculate net offset + net_mins_offset = hours_offset * 60 + net_mins_offset += mins_offset + net_mins_offset *= plus_minus + + # Create an offset object + tzoffset = FixedOffsetTimezone(net_mins_offset) + + # Store the offset in a datetime object + dt = dt.replace(tzinfo=tzoffset) + + return dt + + +class Message: + """Representation of a single message in a catalog.""" + + def __init__( + self, + id: _MessageID, + string: _MessageID | None = '', + locations: Iterable[tuple[str, int]] = (), + flags: Iterable[str] = (), + auto_comments: Iterable[str] = (), + user_comments: Iterable[str] = (), + previous_id: _MessageID = (), + lineno: int | None = None, + context: str | None = None, + ) -> None: + """Create the message object. + + :param id: the message ID, or a ``(singular, plural)`` tuple for + pluralizable messages + :param string: the translated message string, or a + ``(singular, plural)`` tuple for pluralizable messages + :param locations: a sequence of ``(filename, lineno)`` tuples + :param flags: a set or sequence of flags + :param auto_comments: a sequence of automatic comments for the message + :param user_comments: a sequence of user comments for the message + :param previous_id: the previous message ID, or a ``(singular, plural)`` + tuple for pluralizable messages + :param lineno: the line number on which the msgid line was found in the + PO file, if any + :param context: the message context + """ + self.id = id + if not string and self.pluralizable: + string = ('', '') + self.string = string + self.locations = list(distinct(locations)) + self.flags = set(flags) + if id and self.python_format: + self.flags.add('python-format') + else: + self.flags.discard('python-format') + if id and self.python_brace_format: + self.flags.add('python-brace-format') + else: + self.flags.discard('python-brace-format') + self.auto_comments = list(distinct(auto_comments)) + self.user_comments = list(distinct(user_comments)) + if isinstance(previous_id, str): + self.previous_id = [previous_id] + else: + self.previous_id = list(previous_id) + self.lineno = lineno + self.context = context + + def __repr__(self) -> str: + return f"<{type(self).__name__} {self.id!r} (flags: {list(self.flags)!r})>" + + def __cmp__(self, other: object) -> int: + """Compare Messages, taking into account plural ids""" + def values_to_compare(obj): + if isinstance(obj, Message) and obj.pluralizable: + return obj.id[0], obj.context or '' + return obj.id, obj.context or '' + return _cmp(values_to_compare(self), values_to_compare(other)) + + def __gt__(self, other: object) -> bool: + return self.__cmp__(other) > 0 + + def __lt__(self, other: object) -> bool: + return self.__cmp__(other) < 0 + + def __ge__(self, other: object) -> bool: + return self.__cmp__(other) >= 0 + + def __le__(self, other: object) -> bool: + return self.__cmp__(other) <= 0 + + def __eq__(self, other: object) -> bool: + return self.__cmp__(other) == 0 + + def __ne__(self, other: object) -> bool: + return self.__cmp__(other) != 0 + + def is_identical(self, other: Message) -> bool: + """Checks whether messages are identical, taking into account all + properties. + """ + assert isinstance(other, Message) + return self.__dict__ == other.__dict__ + + def clone(self) -> Message: + return Message(*map(copy, (self.id, self.string, self.locations, + self.flags, self.auto_comments, + self.user_comments, self.previous_id, + self.lineno, self.context))) + + def check(self, catalog: Catalog | None = None) -> list[TranslationError]: + """Run various validation checks on the message. Some validations + are only performed if the catalog is provided. This method returns + a sequence of `TranslationError` objects. + + :rtype: ``iterator`` + :param catalog: A catalog instance that is passed to the checkers + :see: `Catalog.check` for a way to perform checks for all messages + in a catalog. + """ + from babel.messages.checkers import checkers + errors: list[TranslationError] = [] + for checker in checkers: + try: + checker(catalog, self) + except TranslationError as e: + errors.append(e) + return errors + + @property + def fuzzy(self) -> bool: + """Whether the translation is fuzzy. + + >>> Message('foo').fuzzy + False + >>> msg = Message('foo', 'foo', flags=['fuzzy']) + >>> msg.fuzzy + True + >>> msg + + + :type: `bool`""" + return 'fuzzy' in self.flags + + @property + def pluralizable(self) -> bool: + """Whether the message is plurizable. + + >>> Message('foo').pluralizable + False + >>> Message(('foo', 'bar')).pluralizable + True + + :type: `bool`""" + return isinstance(self.id, (list, tuple)) + + @property + def python_format(self) -> bool: + """Whether the message contains Python-style parameters. + + >>> Message('foo %(name)s bar').python_format + True + >>> Message(('foo %(name)s', 'foo %(name)s')).python_format + True + + :type: `bool`""" + ids = self.id + if not isinstance(ids, (list, tuple)): + ids = [ids] + return any(PYTHON_FORMAT.search(id) for id in ids) + + @property + def python_brace_format(self) -> bool: + """Whether the message contains Python f-string parameters. + + >>> Message('Hello, {name}!').python_brace_format + True + >>> Message(('One apple', '{count} apples')).python_brace_format + True + + :type: `bool`""" + ids = self.id + if not isinstance(ids, (list, tuple)): + ids = [ids] + return any(_has_python_brace_format(id) for id in ids) + + +class TranslationError(Exception): + """Exception thrown by translation checkers when invalid message + translations are encountered.""" + + +DEFAULT_HEADER = """\ +# Translations template for PROJECT. +# Copyright (C) YEAR ORGANIZATION +# This file is distributed under the same license as the PROJECT project. +# FIRST AUTHOR , YEAR. +#""" + + +def parse_separated_header(value: str) -> dict[str, str]: + # Adapted from https://peps.python.org/pep-0594/#cgi + from email.message import Message + m = Message() + m['content-type'] = value + return dict(m.get_params()) + + +def _force_text(s: str | bytes, encoding: str = 'utf-8', errors: str = 'strict') -> str: + if isinstance(s, str): + return s + if isinstance(s, bytes): + return s.decode(encoding, errors) + return str(s) + + +class Catalog: + """Representation of a message catalog.""" + + def __init__( + self, + locale: Locale | str | None = None, + domain: str | None = None, + header_comment: str | None = DEFAULT_HEADER, + project: str | None = None, + version: str | None = None, + copyright_holder: str | None = None, + msgid_bugs_address: str | None = None, + creation_date: datetime.datetime | str | None = None, + revision_date: datetime.datetime | datetime.time | float | str | None = None, + last_translator: str | None = None, + language_team: str | None = None, + charset: str | None = None, + fuzzy: bool = True, + ) -> None: + """Initialize the catalog object. + + :param locale: the locale identifier or `Locale` object, or `None` + if the catalog is not bound to a locale (which basically + means it's a template) + :param domain: the message domain + :param header_comment: the header comment as string, or `None` for the + default header + :param project: the project's name + :param version: the project's version + :param copyright_holder: the copyright holder of the catalog + :param msgid_bugs_address: the email address or URL to submit bug + reports to + :param creation_date: the date the catalog was created + :param revision_date: the date the catalog was revised + :param last_translator: the name and email of the last translator + :param language_team: the name and email of the language team + :param charset: the encoding to use in the output (defaults to utf-8) + :param fuzzy: the fuzzy bit on the catalog header + """ + self.domain = domain + self.locale = locale + self._header_comment = header_comment + self._messages: dict[str | tuple[str, str], Message] = {} + + self.project = project or 'PROJECT' + self.version = version or 'VERSION' + self.copyright_holder = copyright_holder or 'ORGANIZATION' + self.msgid_bugs_address = msgid_bugs_address or 'EMAIL@ADDRESS' + + self.last_translator = last_translator or 'FULL NAME ' + """Name and email address of the last translator.""" + self.language_team = language_team or 'LANGUAGE ' + """Name and email address of the language team.""" + + self.charset = charset or 'utf-8' + + if creation_date is None: + creation_date = datetime.datetime.now(LOCALTZ) + elif isinstance(creation_date, datetime.datetime) and not creation_date.tzinfo: + creation_date = creation_date.replace(tzinfo=LOCALTZ) + self.creation_date = creation_date + if revision_date is None: + revision_date = 'YEAR-MO-DA HO:MI+ZONE' + elif isinstance(revision_date, datetime.datetime) and not revision_date.tzinfo: + revision_date = revision_date.replace(tzinfo=LOCALTZ) + self.revision_date = revision_date + self.fuzzy = fuzzy + + # Dictionary of obsolete messages + self.obsolete: dict[str | tuple[str, str], Message] = {} + self._num_plurals = None + self._plural_expr = None + + def _set_locale(self, locale: Locale | str | None) -> None: + if locale is None: + self._locale_identifier = None + self._locale = None + return + + if isinstance(locale, Locale): + self._locale_identifier = str(locale) + self._locale = locale + return + + if isinstance(locale, str): + self._locale_identifier = str(locale) + try: + self._locale = Locale.parse(locale) + except UnknownLocaleError: + self._locale = None + return + + raise TypeError(f"`locale` must be a Locale, a locale identifier string, or None; got {locale!r}") + + def _get_locale(self) -> Locale | None: + return self._locale + + def _get_locale_identifier(self) -> str | None: + return self._locale_identifier + + locale = property(_get_locale, _set_locale) + locale_identifier = property(_get_locale_identifier) + + def _get_header_comment(self) -> str: + comment = self._header_comment + year = datetime.datetime.now(LOCALTZ).strftime('%Y') + if hasattr(self.revision_date, 'strftime'): + year = self.revision_date.strftime('%Y') + comment = comment.replace('PROJECT', self.project) \ + .replace('VERSION', self.version) \ + .replace('YEAR', year) \ + .replace('ORGANIZATION', self.copyright_holder) + locale_name = (self.locale.english_name if self.locale else self.locale_identifier) + if locale_name: + comment = comment.replace("Translations template", f"{locale_name} translations") + return comment + + def _set_header_comment(self, string: str | None) -> None: + self._header_comment = string + + header_comment = property(_get_header_comment, _set_header_comment, doc="""\ + The header comment for the catalog. + + >>> catalog = Catalog(project='Foobar', version='1.0', + ... copyright_holder='Foo Company') + >>> print(catalog.header_comment) #doctest: +ELLIPSIS + # Translations template for Foobar. + # Copyright (C) ... Foo Company + # This file is distributed under the same license as the Foobar project. + # FIRST AUTHOR , .... + # + + The header can also be set from a string. Any known upper-case variables + will be replaced when the header is retrieved again: + + >>> catalog = Catalog(project='Foobar', version='1.0', + ... copyright_holder='Foo Company') + >>> catalog.header_comment = '''\\ + ... # The POT for my really cool PROJECT project. + ... # Copyright (C) 1990-2003 ORGANIZATION + ... # This file is distributed under the same license as the PROJECT + ... # project. + ... #''' + >>> print(catalog.header_comment) + # The POT for my really cool Foobar project. + # Copyright (C) 1990-2003 Foo Company + # This file is distributed under the same license as the Foobar + # project. + # + + :type: `unicode` + """) + + def _get_mime_headers(self) -> list[tuple[str, str]]: + if isinstance(self.revision_date, (datetime.datetime, datetime.time, int, float)): + revision_date = format_datetime(self.revision_date, 'yyyy-MM-dd HH:mmZ', locale='en') + else: + revision_date = self.revision_date + + language_team = self.language_team + if self.locale_identifier and 'LANGUAGE' in language_team: + language_team = language_team.replace('LANGUAGE', str(self.locale_identifier)) + + headers: list[tuple[str, str]] = [ + ("Project-Id-Version", f"{self.project} {self.version}"), + ('Report-Msgid-Bugs-To', self.msgid_bugs_address), + ('POT-Creation-Date', format_datetime(self.creation_date, 'yyyy-MM-dd HH:mmZ', locale='en')), + ('PO-Revision-Date', revision_date), + ('Last-Translator', self.last_translator), + ] + if self.locale_identifier: + headers.append(('Language', str(self.locale_identifier))) + headers.append(('Language-Team', language_team)) + if self.locale is not None: + headers.append(('Plural-Forms', self.plural_forms)) + headers += [ + ('MIME-Version', '1.0'), + ("Content-Type", f"text/plain; charset={self.charset}"), + ('Content-Transfer-Encoding', '8bit'), + ("Generated-By", f"Babel {VERSION}\n"), + ] + return headers + + def _set_mime_headers(self, headers: Iterable[tuple[str, str]]) -> None: + for name, value in headers: + name = _force_text(name.lower(), encoding=self.charset) + value = _force_text(value, encoding=self.charset) + if name == 'project-id-version': + parts = value.split(' ') + self.project = ' '.join(parts[:-1]) + self.version = parts[-1] + elif name == 'report-msgid-bugs-to': + self.msgid_bugs_address = value + elif name == 'last-translator': + self.last_translator = value + elif name == 'language': + value = value.replace('-', '_') + # The `or None` makes sure that the locale is set to None + # if the header's value is an empty string, which is what + # some tools generate (instead of eliding the empty Language + # header altogether). + self._set_locale(value or None) + elif name == 'language-team': + self.language_team = value + elif name == 'content-type': + params = parse_separated_header(value) + if 'charset' in params: + self.charset = params['charset'].lower() + elif name == 'plural-forms': + params = parse_separated_header(f" ;{value}") + self._num_plurals = int(params.get('nplurals', 2)) + self._plural_expr = params.get('plural', '(n != 1)') + elif name == 'pot-creation-date': + self.creation_date = _parse_datetime_header(value) + elif name == 'po-revision-date': + # Keep the value if it's not the default one + if 'YEAR' not in value: + self.revision_date = _parse_datetime_header(value) + + mime_headers = property(_get_mime_headers, _set_mime_headers, doc="""\ + The MIME headers of the catalog, used for the special ``msgid ""`` entry. + + The behavior of this property changes slightly depending on whether a locale + is set or not, the latter indicating that the catalog is actually a template + for actual translations. + + Here's an example of the output for such a catalog template: + + >>> from babel.dates import UTC + >>> from datetime import datetime + >>> created = datetime(1990, 4, 1, 15, 30, tzinfo=UTC) + >>> catalog = Catalog(project='Foobar', version='1.0', + ... creation_date=created) + >>> for name, value in catalog.mime_headers: + ... print('%s: %s' % (name, value)) + Project-Id-Version: Foobar 1.0 + Report-Msgid-Bugs-To: EMAIL@ADDRESS + POT-Creation-Date: 1990-04-01 15:30+0000 + PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE + Last-Translator: FULL NAME + Language-Team: LANGUAGE + MIME-Version: 1.0 + Content-Type: text/plain; charset=utf-8 + Content-Transfer-Encoding: 8bit + Generated-By: Babel ... + + And here's an example of the output when the locale is set: + + >>> revised = datetime(1990, 8, 3, 12, 0, tzinfo=UTC) + >>> catalog = Catalog(locale='de_DE', project='Foobar', version='1.0', + ... creation_date=created, revision_date=revised, + ... last_translator='John Doe ', + ... language_team='de_DE ') + >>> for name, value in catalog.mime_headers: + ... print('%s: %s' % (name, value)) + Project-Id-Version: Foobar 1.0 + Report-Msgid-Bugs-To: EMAIL@ADDRESS + POT-Creation-Date: 1990-04-01 15:30+0000 + PO-Revision-Date: 1990-08-03 12:00+0000 + Last-Translator: John Doe + Language: de_DE + Language-Team: de_DE + Plural-Forms: nplurals=2; plural=(n != 1); + MIME-Version: 1.0 + Content-Type: text/plain; charset=utf-8 + Content-Transfer-Encoding: 8bit + Generated-By: Babel ... + + :type: `list` + """) + + @property + def num_plurals(self) -> int: + """The number of plurals used by the catalog or locale. + + >>> Catalog(locale='en').num_plurals + 2 + >>> Catalog(locale='ga').num_plurals + 5 + + :type: `int`""" + if self._num_plurals is None: + num = 2 + if self.locale: + num = get_plural(self.locale)[0] + self._num_plurals = num + return self._num_plurals + + @property + def plural_expr(self) -> str: + """The plural expression used by the catalog or locale. + + >>> Catalog(locale='en').plural_expr + '(n != 1)' + >>> Catalog(locale='ga').plural_expr + '(n==1 ? 0 : n==2 ? 1 : n>=3 && n<=6 ? 2 : n>=7 && n<=10 ? 3 : 4)' + >>> Catalog(locale='ding').plural_expr # unknown locale + '(n != 1)' + + :type: `str`""" + if self._plural_expr is None: + expr = '(n != 1)' + if self.locale: + expr = get_plural(self.locale)[1] + self._plural_expr = expr + return self._plural_expr + + @property + def plural_forms(self) -> str: + """Return the plural forms declaration for the locale. + + >>> Catalog(locale='en').plural_forms + 'nplurals=2; plural=(n != 1);' + >>> Catalog(locale='pt_BR').plural_forms + 'nplurals=2; plural=(n > 1);' + + :type: `str`""" + return f"nplurals={self.num_plurals}; plural={self.plural_expr};" + + def __contains__(self, id: _MessageID) -> bool: + """Return whether the catalog has a message with the specified ID.""" + return self._key_for(id) in self._messages + + def __len__(self) -> int: + """The number of messages in the catalog. + + This does not include the special ``msgid ""`` entry.""" + return len(self._messages) + + def __iter__(self) -> Iterator[Message]: + """Iterates through all the entries in the catalog, in the order they + were added, yielding a `Message` object for every entry. + + :rtype: ``iterator``""" + buf = [] + for name, value in self.mime_headers: + buf.append(f"{name}: {value}") + flags = set() + if self.fuzzy: + flags |= {'fuzzy'} + yield Message('', '\n'.join(buf), flags=flags) + for key in self._messages: + yield self._messages[key] + + def __repr__(self) -> str: + locale = '' + if self.locale: + locale = f" {self.locale}" + return f"<{type(self).__name__} {self.domain!r}{locale}>" + + def __delitem__(self, id: _MessageID) -> None: + """Delete the message with the specified ID.""" + self.delete(id) + + def __getitem__(self, id: _MessageID) -> Message: + """Return the message with the specified ID. + + :param id: the message ID + """ + return self.get(id) + + def __setitem__(self, id: _MessageID, message: Message) -> None: + """Add or update the message with the specified ID. + + >>> catalog = Catalog() + >>> catalog[u'foo'] = Message(u'foo') + >>> catalog[u'foo'] + + + If a message with that ID is already in the catalog, it is updated + to include the locations and flags of the new message. + + >>> catalog = Catalog() + >>> catalog[u'foo'] = Message(u'foo', locations=[('main.py', 1)]) + >>> catalog[u'foo'].locations + [('main.py', 1)] + >>> catalog[u'foo'] = Message(u'foo', locations=[('utils.py', 5)]) + >>> catalog[u'foo'].locations + [('main.py', 1), ('utils.py', 5)] + + :param id: the message ID + :param message: the `Message` object + """ + assert isinstance(message, Message), 'expected a Message object' + key = self._key_for(id, message.context) + current = self._messages.get(key) + if current: + if message.pluralizable and not current.pluralizable: + # The new message adds pluralization + current.id = message.id + current.string = message.string + current.locations = list(distinct(current.locations + + message.locations)) + current.auto_comments = list(distinct(current.auto_comments + + message.auto_comments)) + current.user_comments = list(distinct(current.user_comments + + message.user_comments)) + current.flags |= message.flags + elif id == '': + # special treatment for the header message + self.mime_headers = message_from_string(message.string).items() + self.header_comment = "\n".join([f"# {c}".rstrip() for c in message.user_comments]) + self.fuzzy = message.fuzzy + else: + if isinstance(id, (list, tuple)): + assert isinstance(message.string, (list, tuple)), \ + f"Expected sequence but got {type(message.string)}" + self._messages[key] = message + + def add( + self, + id: _MessageID, + string: _MessageID | None = None, + locations: Iterable[tuple[str, int]] = (), + flags: Iterable[str] = (), + auto_comments: Iterable[str] = (), + user_comments: Iterable[str] = (), + previous_id: _MessageID = (), + lineno: int | None = None, + context: str | None = None, + ) -> Message: + """Add or update the message with the specified ID. + + >>> catalog = Catalog() + >>> catalog.add(u'foo') + + >>> catalog[u'foo'] + + + This method simply constructs a `Message` object with the given + arguments and invokes `__setitem__` with that object. + + :param id: the message ID, or a ``(singular, plural)`` tuple for + pluralizable messages + :param string: the translated message string, or a + ``(singular, plural)`` tuple for pluralizable messages + :param locations: a sequence of ``(filename, lineno)`` tuples + :param flags: a set or sequence of flags + :param auto_comments: a sequence of automatic comments + :param user_comments: a sequence of user comments + :param previous_id: the previous message ID, or a ``(singular, plural)`` + tuple for pluralizable messages + :param lineno: the line number on which the msgid line was found in the + PO file, if any + :param context: the message context + """ + message = Message(id, string, list(locations), flags, auto_comments, + user_comments, previous_id, lineno=lineno, + context=context) + self[id] = message + return message + + def check(self) -> Iterable[tuple[Message, list[TranslationError]]]: + """Run various validation checks on the translations in the catalog. + + For every message which fails validation, this method yield a + ``(message, errors)`` tuple, where ``message`` is the `Message` object + and ``errors`` is a sequence of `TranslationError` objects. + + :rtype: ``generator`` of ``(message, errors)`` + """ + for message in self._messages.values(): + errors = message.check(catalog=self) + if errors: + yield message, errors + + def get(self, id: _MessageID, context: str | None = None) -> Message | None: + """Return the message with the specified ID and context. + + :param id: the message ID + :param context: the message context, or ``None`` for no context + """ + return self._messages.get(self._key_for(id, context)) + + def delete(self, id: _MessageID, context: str | None = None) -> None: + """Delete the message with the specified ID and context. + + :param id: the message ID + :param context: the message context, or ``None`` for no context + """ + key = self._key_for(id, context) + if key in self._messages: + del self._messages[key] + + def update( + self, + template: Catalog, + no_fuzzy_matching: bool = False, + update_header_comment: bool = False, + keep_user_comments: bool = True, + update_creation_date: bool = True, + ) -> None: + """Update the catalog based on the given template catalog. + + >>> from babel.messages import Catalog + >>> template = Catalog() + >>> template.add('green', locations=[('main.py', 99)]) + + >>> template.add('blue', locations=[('main.py', 100)]) + + >>> template.add(('salad', 'salads'), locations=[('util.py', 42)]) + + >>> catalog = Catalog(locale='de_DE') + >>> catalog.add('blue', u'blau', locations=[('main.py', 98)]) + + >>> catalog.add('head', u'Kopf', locations=[('util.py', 33)]) + + >>> catalog.add(('salad', 'salads'), (u'Salat', u'Salate'), + ... locations=[('util.py', 38)]) + + + >>> catalog.update(template) + >>> len(catalog) + 3 + + >>> msg1 = catalog['green'] + >>> msg1.string + >>> msg1.locations + [('main.py', 99)] + + >>> msg2 = catalog['blue'] + >>> msg2.string + u'blau' + >>> msg2.locations + [('main.py', 100)] + + >>> msg3 = catalog['salad'] + >>> msg3.string + (u'Salat', u'Salate') + >>> msg3.locations + [('util.py', 42)] + + Messages that are in the catalog but not in the template are removed + from the main collection, but can still be accessed via the `obsolete` + member: + + >>> 'head' in catalog + False + >>> list(catalog.obsolete.values()) + [] + + :param template: the reference catalog, usually read from a POT file + :param no_fuzzy_matching: whether to use fuzzy matching of message IDs + :param update_header_comment: whether to copy the header comment from the template + :param keep_user_comments: whether to keep user comments from the old catalog + :param update_creation_date: whether to copy the creation date from the template + """ + messages = self._messages + remaining = messages.copy() + self._messages = {} + + # Prepare for fuzzy matching + fuzzy_candidates = {} + if not no_fuzzy_matching: + for msgid in messages: + if msgid and messages[msgid].string: + key = self._key_for(msgid) + ctxt = messages[msgid].context + fuzzy_candidates[self._to_fuzzy_match_key(key)] = (key, ctxt) + fuzzy_matches = set() + + def _merge(message: Message, oldkey: tuple[str, str] | str, newkey: tuple[str, str] | str) -> None: + message = message.clone() + fuzzy = False + if oldkey != newkey: + fuzzy = True + fuzzy_matches.add(oldkey) + oldmsg = messages.get(oldkey) + assert oldmsg is not None + if isinstance(oldmsg.id, str): + message.previous_id = [oldmsg.id] + else: + message.previous_id = list(oldmsg.id) + else: + oldmsg = remaining.pop(oldkey, None) + assert oldmsg is not None + message.string = oldmsg.string + + if keep_user_comments: + message.user_comments = list(distinct(oldmsg.user_comments)) + + if isinstance(message.id, (list, tuple)): + if not isinstance(message.string, (list, tuple)): + fuzzy = True + message.string = tuple( + [message.string] + ([''] * (len(message.id) - 1)), + ) + elif len(message.string) != self.num_plurals: + fuzzy = True + message.string = tuple(message.string[:len(oldmsg.string)]) + elif isinstance(message.string, (list, tuple)): + fuzzy = True + message.string = message.string[0] + message.flags |= oldmsg.flags + if fuzzy: + message.flags |= {'fuzzy'} + self[message.id] = message + + for message in template: + if message.id: + key = self._key_for(message.id, message.context) + if key in messages: + _merge(message, key, key) + else: + if not no_fuzzy_matching: + # do some fuzzy matching with difflib + matches = get_close_matches( + self._to_fuzzy_match_key(key), + fuzzy_candidates.keys(), + 1, + ) + if matches: + modified_key = matches[0] + newkey, newctxt = fuzzy_candidates[modified_key] + if newctxt is not None: + newkey = newkey, newctxt + _merge(message, newkey, key) + continue + + self[message.id] = message + + for msgid in remaining: + if no_fuzzy_matching or msgid not in fuzzy_matches: + self.obsolete[msgid] = remaining[msgid] + + if update_header_comment: + # Allow the updated catalog's header to be rewritten based on the + # template's header + self.header_comment = template.header_comment + + # Make updated catalog's POT-Creation-Date equal to the template + # used to update the catalog + if update_creation_date: + self.creation_date = template.creation_date + + def _to_fuzzy_match_key(self, key: tuple[str, str] | str) -> str: + """Converts a message key to a string suitable for fuzzy matching.""" + if isinstance(key, tuple): + matchkey = key[0] # just the msgid, no context + else: + matchkey = key + return matchkey.lower().strip() + + def _key_for(self, id: _MessageID, context: str | None = None) -> tuple[str, str] | str: + """The key for a message is just the singular ID even for pluralizable + messages, but is a ``(msgid, msgctxt)`` tuple for context-specific + messages. + """ + key = id + if isinstance(key, (list, tuple)): + key = id[0] + if context is not None: + key = (key, context) + return key + + def is_identical(self, other: Catalog) -> bool: + """Checks if catalogs are identical, taking into account messages and + headers. + """ + assert isinstance(other, Catalog) + for key in self._messages.keys() | other._messages.keys(): + message_1 = self.get(key) + message_2 = other.get(key) + if ( + message_1 is None + or message_2 is None + or not message_1.is_identical(message_2) + ): + return False + return dict(self.mime_headers) == dict(other.mime_headers) diff --git a/venv/lib/python3.12/site-packages/babel/messages/checkers.py b/venv/lib/python3.12/site-packages/babel/messages/checkers.py new file mode 100644 index 00000000..df7c3ca7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/babel/messages/checkers.py @@ -0,0 +1,168 @@ +""" + babel.messages.checkers + ~~~~~~~~~~~~~~~~~~~~~~~ + + Various routines that help with validation of translations. + + :since: version 0.9 + + :copyright: (c) 2013-2025 by the Babel Team. + :license: BSD, see LICENSE for more details. +""" +from __future__ import annotations + +from collections.abc import Callable + +from babel.messages.catalog import PYTHON_FORMAT, Catalog, Message, TranslationError + +#: list of format chars that are compatible to each other +_string_format_compatibilities = [ + {'i', 'd', 'u'}, + {'x', 'X'}, + {'f', 'F', 'g', 'G'}, +] + + +def num_plurals(catalog: Catalog | None, message: Message) -> None: + """Verify the number of plurals in the translation.""" + if not message.pluralizable: + if not isinstance(message.string, str): + raise TranslationError("Found plural forms for non-pluralizable " + "message") + return + + # skip further tests if no catalog is provided. + elif catalog is None: + return + + msgstrs = message.string + if not isinstance(msgstrs, (list, tuple)): + msgstrs = (msgstrs,) + if len(msgstrs) != catalog.num_plurals: + raise TranslationError("Wrong number of plural forms (expected %d)" % + catalog.num_plurals) + + +def python_format(catalog: Catalog | None, message: Message) -> None: + """Verify the format string placeholders in the translation.""" + if 'python-format' not in message.flags: + return + msgids = message.id + if not isinstance(msgids, (list, tuple)): + msgids = (msgids,) + msgstrs = message.string + if not isinstance(msgstrs, (list, tuple)): + msgstrs = (msgstrs,) + + for msgid, msgstr in zip(msgids, msgstrs): + if msgstr: + _validate_format(msgid, msgstr) + + +def _validate_format(format: str, alternative: str) -> None: + """Test format string `alternative` against `format`. `format` can be the + msgid of a message and `alternative` one of the `msgstr`\\s. The two + arguments are not interchangeable as `alternative` may contain less + placeholders if `format` uses named placeholders. + + If the string formatting of `alternative` is compatible to `format` the + function returns `None`, otherwise a `TranslationError` is raised. + + Examples for compatible format strings: + + >>> _validate_format('Hello %s!', 'Hallo %s!') + >>> _validate_format('Hello %i!', 'Hallo %d!') + + Example for an incompatible format strings: + + >>> _validate_format('Hello %(name)s!', 'Hallo %s!') + Traceback (most recent call last): + ... + TranslationError: the format strings are of different kinds + + This function is used by the `python_format` checker. + + :param format: The original format string + :param alternative: The alternative format string that should be checked + against format + :raises TranslationError: on formatting errors + """ + + def _parse(string: str) -> list[tuple[str, str]]: + result: list[tuple[str, str]] = [] + for match in PYTHON_FORMAT.finditer(string): + name, format, typechar = match.groups() + if typechar == '%' and name is None: + continue + result.append((name, str(typechar))) + return result + + def _compatible(a: str, b: str) -> bool: + if a == b: + return True + for set in _string_format_compatibilities: + if a in set and b in set: + return True + return False + + def _check_positional(results: list[tuple[str, str]]) -> bool: + positional = None + for name, _char in results: + if positional is None: + positional = name is None + else: + if (name is None) != positional: + raise TranslationError('format string mixes positional ' + 'and named placeholders') + return bool(positional) + + a, b = map(_parse, (format, alternative)) + + if not a: + return + + # now check if both strings are positional or named + a_positional, b_positional = map(_check_positional, (a, b)) + if a_positional and not b_positional and not b: + raise TranslationError('placeholders are incompatible') + elif a_positional != b_positional: + raise TranslationError('the format strings are of different kinds') + + # if we are operating on positional strings both must have the + # same number of format chars and those must be compatible + if a_positional: + if len(a) != len(b): + raise TranslationError('positional format placeholders are ' + 'unbalanced') + for idx, ((_, first), (_, second)) in enumerate(zip(a, b)): + if not _compatible(first, second): + raise TranslationError('incompatible format for placeholder ' + '%d: %r and %r are not compatible' % + (idx + 1, first, second)) + + # otherwise the second string must not have names the first one + # doesn't have and the types of those included must be compatible + else: + type_map = dict(a) + for name, typechar in b: + if name not in type_map: + raise TranslationError(f'unknown named placeholder {name!r}') + elif not _compatible(typechar, type_map[name]): + raise TranslationError( + f'incompatible format for placeholder {name!r}: ' + f'{typechar!r} and {type_map[name]!r} are not compatible', + ) + + +def _find_checkers() -> list[Callable[[Catalog | None, Message], object]]: + from babel.messages._compat import find_entrypoints + checkers: list[Callable[[Catalog | None, Message], object]] = [] + checkers.extend(load() for (name, load) in find_entrypoints('babel.checkers')) + if len(checkers) == 0: + # if entrypoints are not available or no usable egg-info was found + # (see #230), just resort to hard-coded checkers + return [num_plurals, python_format] + return checkers + + +checkers: list[Callable[[Catalog | None, Message], object]] = _find_checkers() diff --git a/venv/lib/python3.12/site-packages/babel/messages/extract.py b/venv/lib/python3.12/site-packages/babel/messages/extract.py new file mode 100644 index 00000000..7f4230f6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/babel/messages/extract.py @@ -0,0 +1,852 @@ +""" + babel.messages.extract + ~~~~~~~~~~~~~~~~~~~~~~ + + Basic infrastructure for extracting localizable messages from source files. + + This module defines an extensible system for collecting localizable message + strings from a variety of sources. A native extractor for Python source + files is builtin, extractors for other sources can be added using very + simple plugins. + + The main entry points into the extraction functionality are the functions + `extract_from_dir` and `extract_from_file`. + + :copyright: (c) 2013-2025 by the Babel Team. + :license: BSD, see LICENSE for more details. +""" +from __future__ import annotations + +import ast +import io +import os +import sys +import tokenize +from collections.abc import ( + Callable, + Collection, + Generator, + Iterable, + Mapping, + MutableSequence, +) +from functools import lru_cache +from os.path import relpath +from textwrap import dedent +from tokenize import COMMENT, NAME, NL, OP, STRING, generate_tokens +from typing import TYPE_CHECKING, Any, TypedDict + +from babel.messages._compat import find_entrypoints +from babel.util import parse_encoding, parse_future_flags, pathmatch + +if TYPE_CHECKING: + from typing import IO, Final, Protocol + + from _typeshed import SupportsItems, SupportsRead, SupportsReadline + from typing_extensions import TypeAlias + + class _PyOptions(TypedDict, total=False): + encoding: str + + class _JSOptions(TypedDict, total=False): + encoding: str + jsx: bool + template_string: bool + parse_template_string: bool + + class _FileObj(SupportsRead[bytes], SupportsReadline[bytes], Protocol): + def seek(self, __offset: int, __whence: int = ...) -> int: ... + def tell(self) -> int: ... + + _SimpleKeyword: TypeAlias = tuple[int | tuple[int, int] | tuple[int, str], ...] | None + _Keyword: TypeAlias = dict[int | None, _SimpleKeyword] | _SimpleKeyword + + # 5-tuple of (filename, lineno, messages, comments, context) + _FileExtractionResult: TypeAlias = tuple[str, int, str | tuple[str, ...], list[str], str | None] + + # 4-tuple of (lineno, message, comments, context) + _ExtractionResult: TypeAlias = tuple[int, str | tuple[str, ...], list[str], str | None] + + # Required arguments: fileobj, keywords, comment_tags, options + # Return value: Iterable of (lineno, message, comments, context) + _CallableExtractionMethod: TypeAlias = Callable[ + [_FileObj | IO[bytes], Mapping[str, _Keyword], Collection[str], Mapping[str, Any]], + Iterable[_ExtractionResult], + ] + + _ExtractionMethod: TypeAlias = _CallableExtractionMethod | str + +GROUP_NAME: Final[str] = 'babel.extractors' + +DEFAULT_KEYWORDS: dict[str, _Keyword] = { + '_': None, + 'gettext': None, + 'ngettext': (1, 2), + 'ugettext': None, + 'ungettext': (1, 2), + 'dgettext': (2,), + 'dngettext': (2, 3), + 'N_': None, + 'pgettext': ((1, 'c'), 2), + 'npgettext': ((1, 'c'), 2, 3), +} + +DEFAULT_MAPPING: list[tuple[str, str]] = [('**.py', 'python')] + +# New tokens in Python 3.12, or None on older versions +FSTRING_START = getattr(tokenize, "FSTRING_START", None) +FSTRING_MIDDLE = getattr(tokenize, "FSTRING_MIDDLE", None) +FSTRING_END = getattr(tokenize, "FSTRING_END", None) + + +def _strip_comment_tags(comments: MutableSequence[str], tags: Iterable[str]): + """Helper function for `extract` that strips comment tags from strings + in a list of comment lines. This functions operates in-place. + """ + def _strip(line: str): + for tag in tags: + if line.startswith(tag): + return line[len(tag):].strip() + return line + comments[:] = map(_strip, comments) + + +def default_directory_filter(dirpath: str | os.PathLike[str]) -> bool: + subdir = os.path.basename(dirpath) + # Legacy default behavior: ignore dot and underscore directories + return not (subdir.startswith('.') or subdir.startswith('_')) + + +def extract_from_dir( + dirname: str | os.PathLike[str] | None = None, + method_map: Iterable[tuple[str, str]] = DEFAULT_MAPPING, + options_map: SupportsItems[str, dict[str, Any]] | None = None, + keywords: Mapping[str, _Keyword] = DEFAULT_KEYWORDS, + comment_tags: Collection[str] = (), + callback: Callable[[str, str, dict[str, Any]], object] | None = None, + strip_comment_tags: bool = False, + directory_filter: Callable[[str], bool] | None = None, +) -> Generator[_FileExtractionResult, None, None]: + """Extract messages from any source files found in the given directory. + + This function generates tuples of the form ``(filename, lineno, message, + comments, context)``. + + Which extraction method is used per file is determined by the `method_map` + parameter, which maps extended glob patterns to extraction method names. + For example, the following is the default mapping: + + >>> method_map = [ + ... ('**.py', 'python') + ... ] + + This basically says that files with the filename extension ".py" at any + level inside the directory should be processed by the "python" extraction + method. Files that don't match any of the mapping patterns are ignored. See + the documentation of the `pathmatch` function for details on the pattern + syntax. + + The following extended mapping would also use the "genshi" extraction + method on any file in "templates" subdirectory: + + >>> method_map = [ + ... ('**/templates/**.*', 'genshi'), + ... ('**.py', 'python') + ... ] + + The dictionary provided by the optional `options_map` parameter augments + these mappings. It uses extended glob patterns as keys, and the values are + dictionaries mapping options names to option values (both strings). + + The glob patterns of the `options_map` do not necessarily need to be the + same as those used in the method mapping. For example, while all files in + the ``templates`` folders in an application may be Genshi applications, the + options for those files may differ based on extension: + + >>> options_map = { + ... '**/templates/**.txt': { + ... 'template_class': 'genshi.template:TextTemplate', + ... 'encoding': 'latin-1' + ... }, + ... '**/templates/**.html': { + ... 'include_attrs': '' + ... } + ... } + + :param dirname: the path to the directory to extract messages from. If + not given the current working directory is used. + :param method_map: a list of ``(pattern, method)`` tuples that maps of + extraction method names to extended glob patterns + :param options_map: a dictionary of additional options (optional) + :param keywords: a dictionary mapping keywords (i.e. names of functions + that should be recognized as translation functions) to + tuples that specify which of their arguments contain + localizable strings + :param comment_tags: a list of tags of translator comments to search for + and include in the results + :param callback: a function that is called for every file that message are + extracted from, just before the extraction itself is + performed; the function is passed the filename, the name + of the extraction method and and the options dictionary as + positional arguments, in that order + :param strip_comment_tags: a flag that if set to `True` causes all comment + tags to be removed from the collected comments. + :param directory_filter: a callback to determine whether a directory should + be recursed into. Receives the full directory path; + should return True if the directory is valid. + :see: `pathmatch` + """ + if dirname is None: + dirname = os.getcwd() + if options_map is None: + options_map = {} + if directory_filter is None: + directory_filter = default_directory_filter + + absname = os.path.abspath(dirname) + for root, dirnames, filenames in os.walk(absname): + dirnames[:] = [ + subdir for subdir in dirnames + if directory_filter(os.path.join(root, subdir)) + ] + dirnames.sort() + filenames.sort() + for filename in filenames: + filepath = os.path.join(root, filename).replace(os.sep, '/') + + yield from check_and_call_extract_file( + filepath, + method_map, + options_map, + callback, + keywords, + comment_tags, + strip_comment_tags, + dirpath=absname, + ) + + +def check_and_call_extract_file( + filepath: str | os.PathLike[str], + method_map: Iterable[tuple[str, str]], + options_map: SupportsItems[str, dict[str, Any]], + callback: Callable[[str, str, dict[str, Any]], object] | None, + keywords: Mapping[str, _Keyword], + comment_tags: Collection[str], + strip_comment_tags: bool, + dirpath: str | os.PathLike[str] | None = None, +) -> Generator[_FileExtractionResult, None, None]: + """Checks if the given file matches an extraction method mapping, and if so, calls extract_from_file. + + Note that the extraction method mappings are based relative to dirpath. + So, given an absolute path to a file `filepath`, we want to check using + just the relative path from `dirpath` to `filepath`. + + Yields 5-tuples (filename, lineno, messages, comments, context). + + :param filepath: An absolute path to a file that exists. + :param method_map: a list of ``(pattern, method)`` tuples that maps of + extraction method names to extended glob patterns + :param options_map: a dictionary of additional options (optional) + :param callback: a function that is called for every file that message are + extracted from, just before the extraction itself is + performed; the function is passed the filename, the name + of the extraction method and and the options dictionary as + positional arguments, in that order + :param keywords: a dictionary mapping keywords (i.e. names of functions + that should be recognized as translation functions) to + tuples that specify which of their arguments contain + localizable strings + :param comment_tags: a list of tags of translator comments to search for + and include in the results + :param strip_comment_tags: a flag that if set to `True` causes all comment + tags to be removed from the collected comments. + :param dirpath: the path to the directory to extract messages from. + :return: iterable of 5-tuples (filename, lineno, messages, comments, context) + :rtype: Iterable[tuple[str, int, str|tuple[str], list[str], str|None] + """ + # filename is the relative path from dirpath to the actual file + filename = relpath(filepath, dirpath) + + for pattern, method in method_map: + if not pathmatch(pattern, filename): + continue + + options = {} + for opattern, odict in options_map.items(): + if pathmatch(opattern, filename): + options = odict + break + if callback: + callback(filename, method, options) + for message_tuple in extract_from_file( + method, filepath, + keywords=keywords, + comment_tags=comment_tags, + options=options, + strip_comment_tags=strip_comment_tags, + ): + yield (filename, *message_tuple) + + break + + +def extract_from_file( + method: _ExtractionMethod, + filename: str | os.PathLike[str], + keywords: Mapping[str, _Keyword] = DEFAULT_KEYWORDS, + comment_tags: Collection[str] = (), + options: Mapping[str, Any] | None = None, + strip_comment_tags: bool = False, +) -> list[_ExtractionResult]: + """Extract messages from a specific file. + + This function returns a list of tuples of the form ``(lineno, message, comments, context)``. + + :param filename: the path to the file to extract messages from + :param method: a string specifying the extraction method (.e.g. "python") + :param keywords: a dictionary mapping keywords (i.e. names of functions + that should be recognized as translation functions) to + tuples that specify which of their arguments contain + localizable strings + :param comment_tags: a list of translator tags to search for and include + in the results + :param strip_comment_tags: a flag that if set to `True` causes all comment + tags to be removed from the collected comments. + :param options: a dictionary of additional options (optional) + :returns: list of tuples of the form ``(lineno, message, comments, context)`` + :rtype: list[tuple[int, str|tuple[str], list[str], str|None] + """ + if method == 'ignore': + return [] + + with open(filename, 'rb') as fileobj: + return list(extract(method, fileobj, keywords, comment_tags, + options, strip_comment_tags)) + + +def _match_messages_against_spec( + lineno: int, + messages: list[str | None], + comments: list[str], + fileobj: _FileObj, + spec: tuple[int | tuple[int, str], ...], +): + translatable = [] + context = None + + # last_index is 1 based like the keyword spec + last_index = len(messages) + for index in spec: + if isinstance(index, tuple): # (n, 'c') + context = messages[index[0] - 1] + continue + if last_index < index: + # Not enough arguments + return + message = messages[index - 1] + if message is None: + return + translatable.append(message) + + # keyword spec indexes are 1 based, therefore '-1' + if isinstance(spec[0], tuple): + # context-aware *gettext method + first_msg_index = spec[1] - 1 + else: + first_msg_index = spec[0] - 1 + # An empty string msgid isn't valid, emit a warning + if not messages[first_msg_index]: + filename = (getattr(fileobj, "name", None) or "(unknown)") + sys.stderr.write( + f"{filename}:{lineno}: warning: Empty msgid. It is reserved by GNU gettext: gettext(\"\") " + f"returns the header entry with meta information, not the empty string.\n", + ) + return + + translatable = tuple(translatable) + if len(translatable) == 1: + translatable = translatable[0] + + return lineno, translatable, comments, context + + +@lru_cache(maxsize=None) +def _find_extractor(name: str): + for ep_name, load in find_entrypoints(GROUP_NAME): + if ep_name == name: + return load() + return None + + +def extract( + method: _ExtractionMethod, + fileobj: _FileObj, + keywords: Mapping[str, _Keyword] = DEFAULT_KEYWORDS, + comment_tags: Collection[str] = (), + options: Mapping[str, Any] | None = None, + strip_comment_tags: bool = False, +) -> Generator[_ExtractionResult, None, None]: + """Extract messages from the given file-like object using the specified + extraction method. + + This function returns tuples of the form ``(lineno, message, comments, context)``. + + The implementation dispatches the actual extraction to plugins, based on the + value of the ``method`` parameter. + + >>> source = b'''# foo module + ... def run(argv): + ... print(_('Hello, world!')) + ... ''' + + >>> from io import BytesIO + >>> for message in extract('python', BytesIO(source)): + ... print(message) + (3, u'Hello, world!', [], None) + + :param method: an extraction method (a callable), or + a string specifying the extraction method (.e.g. "python"); + if this is a simple name, the extraction function will be + looked up by entry point; if it is an explicit reference + to a function (of the form ``package.module:funcname`` or + ``package.module.funcname``), the corresponding function + will be imported and used + :param fileobj: the file-like object the messages should be extracted from + :param keywords: a dictionary mapping keywords (i.e. names of functions + that should be recognized as translation functions) to + tuples that specify which of their arguments contain + localizable strings + :param comment_tags: a list of translator tags to search for and include + in the results + :param options: a dictionary of additional options (optional) + :param strip_comment_tags: a flag that if set to `True` causes all comment + tags to be removed from the collected comments. + :raise ValueError: if the extraction method is not registered + :returns: iterable of tuples of the form ``(lineno, message, comments, context)`` + :rtype: Iterable[tuple[int, str|tuple[str], list[str], str|None] + """ + if callable(method): + func = method + elif ':' in method or '.' in method: + if ':' not in method: + lastdot = method.rfind('.') + module, attrname = method[:lastdot], method[lastdot + 1:] + else: + module, attrname = method.split(':', 1) + func = getattr(__import__(module, {}, {}, [attrname]), attrname) + else: + func = _find_extractor(method) + if func is None: + # if no named entry point was found, + # we resort to looking up a builtin extractor + func = _BUILTIN_EXTRACTORS.get(method) + + if func is None: + raise ValueError(f"Unknown extraction method {method!r}") + + results = func(fileobj, keywords.keys(), comment_tags, + options=options or {}) + + for lineno, funcname, messages, comments in results: + if not isinstance(messages, (list, tuple)): + messages = [messages] + if not messages: + continue + + specs = keywords[funcname] or None if funcname else None + # {None: x} may be collapsed into x for backwards compatibility. + if not isinstance(specs, dict): + specs = {None: specs} + + if strip_comment_tags: + _strip_comment_tags(comments, comment_tags) + + # None matches all arities. + for arity in (None, len(messages)): + try: + spec = specs[arity] + except KeyError: + continue + if spec is None: + spec = (1,) + result = _match_messages_against_spec(lineno, messages, comments, fileobj, spec) + if result is not None: + yield result + + +def extract_nothing( + fileobj: _FileObj, + keywords: Mapping[str, _Keyword], + comment_tags: Collection[str], + options: Mapping[str, Any], +) -> list[_ExtractionResult]: + """Pseudo extractor that does not actually extract anything, but simply + returns an empty list. + """ + return [] + + +def extract_python( + fileobj: IO[bytes], + keywords: Mapping[str, _Keyword], + comment_tags: Collection[str], + options: _PyOptions, +) -> Generator[_ExtractionResult, None, None]: + """Extract messages from Python source code. + + It returns an iterator yielding tuples in the following form ``(lineno, + funcname, message, comments)``. + + :param fileobj: the seekable, file-like object the messages should be + extracted from + :param keywords: a list of keywords (i.e. function names) that should be + recognized as translation functions + :param comment_tags: a list of translator tags to search for and include + in the results + :param options: a dictionary of additional options (optional) + :rtype: ``iterator`` + """ + funcname = lineno = message_lineno = None + call_stack = -1 + buf = [] + messages = [] + translator_comments = [] + in_def = in_translator_comments = False + comment_tag = None + + encoding = parse_encoding(fileobj) or options.get('encoding', 'UTF-8') + future_flags = parse_future_flags(fileobj, encoding) + next_line = lambda: fileobj.readline().decode(encoding) + + tokens = generate_tokens(next_line) + + # Current prefix of a Python 3.12 (PEP 701) f-string, or None if we're not + # currently parsing one. + current_fstring_start = None + + for tok, value, (lineno, _), _, _ in tokens: + if call_stack == -1 and tok == NAME and value in ('def', 'class'): + in_def = True + elif tok == OP and value == '(': + if in_def: + # Avoid false positives for declarations such as: + # def gettext(arg='message'): + in_def = False + continue + if funcname: + call_stack += 1 + elif in_def and tok == OP and value == ':': + # End of a class definition without parens + in_def = False + continue + elif call_stack == -1 and tok == COMMENT: + # Strip the comment token from the line + value = value[1:].strip() + if in_translator_comments and \ + translator_comments[-1][0] == lineno - 1: + # We're already inside a translator comment, continue appending + translator_comments.append((lineno, value)) + continue + # If execution reaches this point, let's see if comment line + # starts with one of the comment tags + for comment_tag in comment_tags: + if value.startswith(comment_tag): + in_translator_comments = True + translator_comments.append((lineno, value)) + break + elif funcname and call_stack == 0: + nested = (tok == NAME and value in keywords) + if (tok == OP and value == ')') or nested: + if buf: + messages.append(''.join(buf)) + del buf[:] + else: + messages.append(None) + + messages = tuple(messages) if len(messages) > 1 else messages[0] + # Comments don't apply unless they immediately + # precede the message + if translator_comments and \ + translator_comments[-1][0] < message_lineno - 1: + translator_comments = [] + + yield (message_lineno, funcname, messages, + [comment[1] for comment in translator_comments]) + + funcname = lineno = message_lineno = None + call_stack = -1 + messages = [] + translator_comments = [] + in_translator_comments = False + if nested: + funcname = value + elif tok == STRING: + val = _parse_python_string(value, encoding, future_flags) + if val is not None: + if not message_lineno: + message_lineno = lineno + buf.append(val) + + # Python 3.12+, see https://peps.python.org/pep-0701/#new-tokens + elif tok == FSTRING_START: + current_fstring_start = value + if not message_lineno: + message_lineno = lineno + elif tok == FSTRING_MIDDLE: + if current_fstring_start is not None: + current_fstring_start += value + elif tok == FSTRING_END: + if current_fstring_start is not None: + fstring = current_fstring_start + value + val = _parse_python_string(fstring, encoding, future_flags) + if val is not None: + buf.append(val) + + elif tok == OP and value == ',': + if buf: + messages.append(''.join(buf)) + del buf[:] + else: + messages.append(None) + if translator_comments: + # We have translator comments, and since we're on a + # comma(,) user is allowed to break into a new line + # Let's increase the last comment's lineno in order + # for the comment to still be a valid one + old_lineno, old_comment = translator_comments.pop() + translator_comments.append((old_lineno + 1, old_comment)) + + elif tok != NL and not message_lineno: + message_lineno = lineno + elif call_stack > 0 and tok == OP and value == ')': + call_stack -= 1 + elif funcname and call_stack == -1: + funcname = None + elif tok == NAME and value in keywords: + funcname = value + + if current_fstring_start is not None and tok not in {FSTRING_START, FSTRING_MIDDLE}: + # In Python 3.12, tokens other than FSTRING_* mean the + # f-string is dynamic, so we don't wan't to extract it. + # And if it's FSTRING_END, we've already handled it above. + # Let's forget that we're in an f-string. + current_fstring_start = None + + +def _parse_python_string(value: str, encoding: str, future_flags: int) -> str | None: + # Unwrap quotes in a safe manner, maintaining the string's encoding + # https://sourceforge.net/tracker/?func=detail&atid=355470&aid=617979&group_id=5470 + code = compile( + f'# coding={str(encoding)}\n{value}', + '', + 'eval', + ast.PyCF_ONLY_AST | future_flags, + ) + if isinstance(code, ast.Expression): + body = code.body + if isinstance(body, ast.Constant): + return body.value + if isinstance(body, ast.JoinedStr): # f-string + if all(isinstance(node, ast.Constant) for node in body.values): + return ''.join(node.value for node in body.values) + # TODO: we could raise an error or warning when not all nodes are constants + return None + + +def extract_javascript( + fileobj: _FileObj, + keywords: Mapping[str, _Keyword], + comment_tags: Collection[str], + options: _JSOptions, + lineno: int = 1, +) -> Generator[_ExtractionResult, None, None]: + """Extract messages from JavaScript source code. + + :param fileobj: the seekable, file-like object the messages should be + extracted from + :param keywords: a list of keywords (i.e. function names) that should be + recognized as translation functions + :param comment_tags: a list of translator tags to search for and include + in the results + :param options: a dictionary of additional options (optional) + Supported options are: + * `jsx` -- set to false to disable JSX/E4X support. + * `template_string` -- if `True`, supports gettext(`key`) + * `parse_template_string` -- if `True` will parse the + contents of javascript + template strings. + :param lineno: line number offset (for parsing embedded fragments) + """ + from babel.messages.jslexer import Token, tokenize, unquote_string + funcname = message_lineno = None + messages = [] + last_argument = None + translator_comments = [] + concatenate_next = False + encoding = options.get('encoding', 'utf-8') + last_token = None + call_stack = -1 + dotted = any('.' in kw for kw in keywords) + for token in tokenize( + fileobj.read().decode(encoding), + jsx=options.get("jsx", True), + template_string=options.get("template_string", True), + dotted=dotted, + lineno=lineno, + ): + if ( # Turn keyword`foo` expressions into keyword("foo") calls: + funcname and # have a keyword... + (last_token and last_token.type == 'name') and # we've seen nothing after the keyword... + token.type == 'template_string' # this is a template string + ): + message_lineno = token.lineno + messages = [unquote_string(token.value)] + call_stack = 0 + token = Token('operator', ')', token.lineno) + + if options.get('parse_template_string') and not funcname and token.type == 'template_string': + yield from parse_template_string(token.value, keywords, comment_tags, options, token.lineno) + + elif token.type == 'operator' and token.value == '(': + if funcname: + message_lineno = token.lineno + call_stack += 1 + + elif call_stack == -1 and token.type == 'linecomment': + value = token.value[2:].strip() + if translator_comments and \ + translator_comments[-1][0] == token.lineno - 1: + translator_comments.append((token.lineno, value)) + continue + + for comment_tag in comment_tags: + if value.startswith(comment_tag): + translator_comments.append((token.lineno, value.strip())) + break + + elif token.type == 'multilinecomment': + # only one multi-line comment may precede a translation + translator_comments = [] + value = token.value[2:-2].strip() + for comment_tag in comment_tags: + if value.startswith(comment_tag): + lines = value.splitlines() + if lines: + lines[0] = lines[0].strip() + lines[1:] = dedent('\n'.join(lines[1:])).splitlines() + for offset, line in enumerate(lines): + translator_comments.append((token.lineno + offset, + line)) + break + + elif funcname and call_stack == 0: + if token.type == 'operator' and token.value == ')': + if last_argument is not None: + messages.append(last_argument) + if len(messages) > 1: + messages = tuple(messages) + elif messages: + messages = messages[0] + else: + messages = None + + # Comments don't apply unless they immediately precede the + # message + if translator_comments and \ + translator_comments[-1][0] < message_lineno - 1: + translator_comments = [] + + if messages is not None: + yield (message_lineno, funcname, messages, + [comment[1] for comment in translator_comments]) + + funcname = message_lineno = last_argument = None + concatenate_next = False + translator_comments = [] + messages = [] + call_stack = -1 + + elif token.type in ('string', 'template_string'): + new_value = unquote_string(token.value) + if concatenate_next: + last_argument = (last_argument or '') + new_value + concatenate_next = False + else: + last_argument = new_value + + elif token.type == 'operator': + if token.value == ',': + if last_argument is not None: + messages.append(last_argument) + last_argument = None + else: + messages.append(None) + concatenate_next = False + elif token.value == '+': + concatenate_next = True + + elif call_stack > 0 and token.type == 'operator' \ + and token.value == ')': + call_stack -= 1 + + elif funcname and call_stack == -1: + funcname = None + + elif call_stack == -1 and token.type == 'name' and \ + token.value in keywords and \ + (last_token is None or last_token.type != 'name' or + last_token.value != 'function'): + funcname = token.value + + last_token = token + + +def parse_template_string( + template_string: str, + keywords: Mapping[str, _Keyword], + comment_tags: Collection[str], + options: _JSOptions, + lineno: int = 1, +) -> Generator[_ExtractionResult, None, None]: + """Parse JavaScript template string. + + :param template_string: the template string to be parsed + :param keywords: a list of keywords (i.e. function names) that should be + recognized as translation functions + :param comment_tags: a list of translator tags to search for and include + in the results + :param options: a dictionary of additional options (optional) + :param lineno: starting line number (optional) + """ + from babel.messages.jslexer import line_re + prev_character = None + level = 0 + inside_str = False + expression_contents = '' + for character in template_string[1:-1]: + if not inside_str and character in ('"', "'", '`'): + inside_str = character + elif inside_str == character and prev_character != r'\\': + inside_str = False + if level: + expression_contents += character + if not inside_str: + if character == '{' and prev_character == '$': + level += 1 + elif level and character == '}': + level -= 1 + if level == 0 and expression_contents: + expression_contents = expression_contents[0:-1] + fake_file_obj = io.BytesIO(expression_contents.encode()) + yield from extract_javascript(fake_file_obj, keywords, comment_tags, options, lineno) + lineno += len(line_re.findall(expression_contents)) + expression_contents = '' + prev_character = character + + +_BUILTIN_EXTRACTORS = { + 'ignore': extract_nothing, + 'python': extract_python, + 'javascript': extract_javascript, +} diff --git a/venv/lib/python3.12/site-packages/babel/messages/frontend.py b/venv/lib/python3.12/site-packages/babel/messages/frontend.py new file mode 100644 index 00000000..29e5a2aa --- /dev/null +++ b/venv/lib/python3.12/site-packages/babel/messages/frontend.py @@ -0,0 +1,1202 @@ +""" + babel.messages.frontend + ~~~~~~~~~~~~~~~~~~~~~~~ + + Frontends for the message extraction functionality. + + :copyright: (c) 2013-2025 by the Babel Team. + :license: BSD, see LICENSE for more details. +""" + +from __future__ import annotations + +import datetime +import fnmatch +import logging +import optparse +import os +import re +import shutil +import sys +import tempfile +import warnings +from configparser import RawConfigParser +from io import StringIO +from typing import BinaryIO, Iterable, Literal + +from babel import Locale, localedata +from babel import __version__ as VERSION +from babel.core import UnknownLocaleError +from babel.messages.catalog import DEFAULT_HEADER, Catalog +from babel.messages.extract import ( + DEFAULT_KEYWORDS, + DEFAULT_MAPPING, + check_and_call_extract_file, + extract_from_dir, +) +from babel.messages.mofile import write_mo +from babel.messages.pofile import read_po, write_po +from babel.util import LOCALTZ + +log = logging.getLogger('babel') + + +class BaseError(Exception): + pass + + +class OptionError(BaseError): + pass + + +class SetupError(BaseError): + pass + + +class ConfigurationError(BaseError): + """ + Raised for errors in configuration files. + """ + + +def listify_value(arg, split=None): + """ + Make a list out of an argument. + + Values from `distutils` argument parsing are always single strings; + values from `optparse` parsing may be lists of strings that may need + to be further split. + + No matter the input, this function returns a flat list of whitespace-trimmed + strings, with `None` values filtered out. + + >>> listify_value("foo bar") + ['foo', 'bar'] + >>> listify_value(["foo bar"]) + ['foo', 'bar'] + >>> listify_value([["foo"], "bar"]) + ['foo', 'bar'] + >>> listify_value([["foo"], ["bar", None, "foo"]]) + ['foo', 'bar', 'foo'] + >>> listify_value("foo, bar, quux", ",") + ['foo', 'bar', 'quux'] + + :param arg: A string or a list of strings + :param split: The argument to pass to `str.split()`. + :return: + """ + out = [] + + if not isinstance(arg, (list, tuple)): + arg = [arg] + + for val in arg: + if val is None: + continue + if isinstance(val, (list, tuple)): + out.extend(listify_value(val, split=split)) + continue + out.extend(s.strip() for s in str(val).split(split)) + assert all(isinstance(val, str) for val in out) + return out + + +class CommandMixin: + # This class is a small shim between Distutils commands and + # optparse option parsing in the frontend command line. + + #: Option name to be input as `args` on the script command line. + as_args = None + + #: Options which allow multiple values. + #: This is used by the `optparse` transmogrification code. + multiple_value_options = () + + #: Options which are booleans. + #: This is used by the `optparse` transmogrification code. + # (This is actually used by distutils code too, but is never + # declared in the base class.) + boolean_options = () + + #: Option aliases, to retain standalone command compatibility. + #: Distutils does not support option aliases, but optparse does. + #: This maps the distutils argument name to an iterable of aliases + #: that are usable with optparse. + option_aliases = {} + + #: Choices for options that needed to be restricted to specific + #: list of choices. + option_choices = {} + + #: Log object. To allow replacement in the script command line runner. + log = log + + def __init__(self, dist=None): + # A less strict version of distutils' `__init__`. + self.distribution = dist + self.initialize_options() + self._dry_run = None + self.verbose = False + self.force = None + self.help = 0 + self.finalized = 0 + + def initialize_options(self): + pass + + def ensure_finalized(self): + if not self.finalized: + self.finalize_options() + self.finalized = 1 + + def finalize_options(self): + raise RuntimeError( + f"abstract method -- subclass {self.__class__} must override", + ) + + +class CompileCatalog(CommandMixin): + description = 'compile message catalogs to binary MO files' + user_options = [ + ('domain=', 'D', + "domains of PO files (space separated list, default 'messages')"), + ('directory=', 'd', + 'path to base directory containing the catalogs'), + ('input-file=', 'i', + 'name of the input file'), + ('output-file=', 'o', + "name of the output file (default " + "'//LC_MESSAGES/.mo')"), + ('locale=', 'l', + 'locale of the catalog to compile'), + ('use-fuzzy', 'f', + 'also include fuzzy translations'), + ('statistics', None, + 'print statistics about translations'), + ] + boolean_options = ['use-fuzzy', 'statistics'] + + def initialize_options(self): + self.domain = 'messages' + self.directory = None + self.input_file = None + self.output_file = None + self.locale = None + self.use_fuzzy = False + self.statistics = False + + def finalize_options(self): + self.domain = listify_value(self.domain) + if not self.input_file and not self.directory: + raise OptionError('you must specify either the input file or the base directory') + if not self.output_file and not self.directory: + raise OptionError('you must specify either the output file or the base directory') + + def run(self): + n_errors = 0 + for domain in self.domain: + for errors in self._run_domain(domain).values(): + n_errors += len(errors) + if n_errors: + self.log.error('%d errors encountered.', n_errors) + return (1 if n_errors else 0) + + def _run_domain(self, domain): + po_files = [] + mo_files = [] + + if not self.input_file: + if self.locale: + po_files.append((self.locale, + os.path.join(self.directory, self.locale, + 'LC_MESSAGES', + f"{domain}.po"))) + mo_files.append(os.path.join(self.directory, self.locale, + 'LC_MESSAGES', + f"{domain}.mo")) + else: + for locale in os.listdir(self.directory): + po_file = os.path.join(self.directory, locale, + 'LC_MESSAGES', f"{domain}.po") + if os.path.exists(po_file): + po_files.append((locale, po_file)) + mo_files.append(os.path.join(self.directory, locale, + 'LC_MESSAGES', + f"{domain}.mo")) + else: + po_files.append((self.locale, self.input_file)) + if self.output_file: + mo_files.append(self.output_file) + else: + mo_files.append(os.path.join(self.directory, self.locale, + 'LC_MESSAGES', + f"{domain}.mo")) + + if not po_files: + raise OptionError('no message catalogs found') + + catalogs_and_errors = {} + + for idx, (locale, po_file) in enumerate(po_files): + mo_file = mo_files[idx] + with open(po_file, 'rb') as infile: + catalog = read_po(infile, locale) + + if self.statistics: + translated = 0 + for message in list(catalog)[1:]: + if message.string: + translated += 1 + percentage = 0 + if len(catalog): + percentage = translated * 100 // len(catalog) + self.log.info( + '%d of %d messages (%d%%) translated in %s', + translated, len(catalog), percentage, po_file, + ) + + if catalog.fuzzy and not self.use_fuzzy: + self.log.info('catalog %s is marked as fuzzy, skipping', po_file) + continue + + catalogs_and_errors[catalog] = catalog_errors = list(catalog.check()) + for message, errors in catalog_errors: + for error in errors: + self.log.error( + 'error: %s:%d: %s', po_file, message.lineno, error, + ) + + self.log.info('compiling catalog %s to %s', po_file, mo_file) + + with open(mo_file, 'wb') as outfile: + write_mo(outfile, catalog, use_fuzzy=self.use_fuzzy) + + return catalogs_and_errors + + +def _make_directory_filter(ignore_patterns): + """ + Build a directory_filter function based on a list of ignore patterns. + """ + + def cli_directory_filter(dirname): + basename = os.path.basename(dirname) + return not any( + fnmatch.fnmatch(basename, ignore_pattern) + for ignore_pattern + in ignore_patterns + ) + + return cli_directory_filter + + +class ExtractMessages(CommandMixin): + description = 'extract localizable strings from the project code' + user_options = [ + ('charset=', None, + 'charset to use in the output file (default "utf-8")'), + ('keywords=', 'k', + 'space-separated list of keywords to look for in addition to the ' + 'defaults (may be repeated multiple times)'), + ('no-default-keywords', None, + 'do not include the default keywords'), + ('mapping-file=', 'F', + 'path to the mapping configuration file'), + ('no-location', None, + 'do not include location comments with filename and line number'), + ('add-location=', None, + 'location lines format. If it is not given or "full", it generates ' + 'the lines with both file name and line number. If it is "file", ' + 'the line number part is omitted. If it is "never", it completely ' + 'suppresses the lines (same as --no-location).'), + ('omit-header', None, + 'do not include msgid "" entry in header'), + ('output-file=', 'o', + 'name of the output file'), + ('width=', 'w', + 'set output line width (default 76)'), + ('no-wrap', None, + 'do not break long message lines, longer than the output line width, ' + 'into several lines'), + ('sort-output', None, + 'generate sorted output (default False)'), + ('sort-by-file', None, + 'sort output by file location (default False)'), + ('msgid-bugs-address=', None, + 'set report address for msgid'), + ('copyright-holder=', None, + 'set copyright holder in output'), + ('project=', None, + 'set project name in output'), + ('version=', None, + 'set project version in output'), + ('add-comments=', 'c', + 'place comment block with TAG (or those preceding keyword lines) in ' + 'output file. Separate multiple TAGs with commas(,)'), # TODO: Support repetition of this argument + ('strip-comments', 's', + 'strip the comment TAGs from the comments.'), + ('input-paths=', None, + 'files or directories that should be scanned for messages. Separate multiple ' + 'files or directories with commas(,)'), # TODO: Support repetition of this argument + ('input-dirs=', None, # TODO (3.x): Remove me. + 'alias for input-paths (does allow files as well as directories).'), + ('ignore-dirs=', None, + 'Patterns for directories to ignore when scanning for messages. ' + 'Separate multiple patterns with spaces (default ".* ._")'), + ('header-comment=', None, + 'header comment for the catalog'), + ('last-translator=', None, + 'set the name and email of the last translator in output'), + ] + boolean_options = [ + 'no-default-keywords', 'no-location', 'omit-header', 'no-wrap', + 'sort-output', 'sort-by-file', 'strip-comments', + ] + as_args = 'input-paths' + multiple_value_options = ( + 'add-comments', + 'keywords', + 'ignore-dirs', + ) + option_aliases = { + 'keywords': ('--keyword',), + 'mapping-file': ('--mapping',), + 'output-file': ('--output',), + 'strip-comments': ('--strip-comment-tags',), + 'last-translator': ('--last-translator',), + } + option_choices = { + 'add-location': ('full', 'file', 'never'), + } + + def initialize_options(self): + self.charset = 'utf-8' + self.keywords = None + self.no_default_keywords = False + self.mapping_file = None + self.no_location = False + self.add_location = None + self.omit_header = False + self.output_file = None + self.input_dirs = None + self.input_paths = None + self.width = None + self.no_wrap = False + self.sort_output = False + self.sort_by_file = False + self.msgid_bugs_address = None + self.copyright_holder = None + self.project = None + self.version = None + self.add_comments = None + self.strip_comments = False + self.include_lineno = True + self.ignore_dirs = None + self.header_comment = None + self.last_translator = None + + def finalize_options(self): + if self.input_dirs: + if not self.input_paths: + self.input_paths = self.input_dirs + else: + raise OptionError( + 'input-dirs and input-paths are mutually exclusive', + ) + + keywords = {} if self.no_default_keywords else DEFAULT_KEYWORDS.copy() + + keywords.update(parse_keywords(listify_value(self.keywords))) + + self.keywords = keywords + + if not self.keywords: + raise OptionError( + 'you must specify new keywords if you disable the default ones', + ) + + if not self.output_file: + raise OptionError('no output file specified') + if self.no_wrap and self.width: + raise OptionError( + "'--no-wrap' and '--width' are mutually exclusive", + ) + if not self.no_wrap and not self.width: + self.width = 76 + elif self.width is not None: + self.width = int(self.width) + + if self.sort_output and self.sort_by_file: + raise OptionError( + "'--sort-output' and '--sort-by-file' are mutually exclusive", + ) + + if self.input_paths: + if isinstance(self.input_paths, str): + self.input_paths = re.split(r',\s*', self.input_paths) + elif self.distribution is not None: + self.input_paths = dict.fromkeys([ + k.split('.', 1)[0] + for k in (self.distribution.packages or ()) + ]).keys() + else: + self.input_paths = [] + + if not self.input_paths: + raise OptionError("no input files or directories specified") + + for path in self.input_paths: + if not os.path.exists(path): + raise OptionError(f"Input path: {path} does not exist") + + self.add_comments = listify_value(self.add_comments or (), ",") + + if self.distribution: + if not self.project: + self.project = self.distribution.get_name() + if not self.version: + self.version = self.distribution.get_version() + + if self.add_location == 'never': + self.no_location = True + elif self.add_location == 'file': + self.include_lineno = False + + ignore_dirs = listify_value(self.ignore_dirs) + if ignore_dirs: + self.directory_filter = _make_directory_filter(ignore_dirs) + else: + self.directory_filter = None + + def _build_callback(self, path: str): + def callback(filename: str, method: str, options: dict): + if method == 'ignore': + return + + # If we explicitly provide a full filepath, just use that. + # Otherwise, path will be the directory path and filename + # is the relative path from that dir to the file. + # So we can join those to get the full filepath. + if os.path.isfile(path): + filepath = path + else: + filepath = os.path.normpath(os.path.join(path, filename)) + + optstr = '' + if options: + opt_values = ", ".join(f'{k}="{v}"' for k, v in options.items()) + optstr = f" ({opt_values})" + self.log.info('extracting messages from %s%s', filepath, optstr) + + return callback + + def run(self): + mappings = self._get_mappings() + with open(self.output_file, 'wb') as outfile: + catalog = Catalog(project=self.project, + version=self.version, + msgid_bugs_address=self.msgid_bugs_address, + copyright_holder=self.copyright_holder, + charset=self.charset, + header_comment=(self.header_comment or DEFAULT_HEADER), + last_translator=self.last_translator) + + for path, method_map, options_map in mappings: + callback = self._build_callback(path) + if os.path.isfile(path): + current_dir = os.getcwd() + extracted = check_and_call_extract_file( + path, method_map, options_map, + callback, self.keywords, self.add_comments, + self.strip_comments, current_dir, + ) + else: + extracted = extract_from_dir( + path, method_map, options_map, + keywords=self.keywords, + comment_tags=self.add_comments, + callback=callback, + strip_comment_tags=self.strip_comments, + directory_filter=self.directory_filter, + ) + for filename, lineno, message, comments, context in extracted: + if os.path.isfile(path): + filepath = filename # already normalized + else: + filepath = os.path.normpath(os.path.join(path, filename)) + + catalog.add(message, None, [(filepath, lineno)], + auto_comments=comments, context=context) + + self.log.info('writing PO template file to %s', self.output_file) + write_po(outfile, catalog, width=self.width, + no_location=self.no_location, + omit_header=self.omit_header, + sort_output=self.sort_output, + sort_by_file=self.sort_by_file, + include_lineno=self.include_lineno) + + def _get_mappings(self): + mappings = [] + + if self.mapping_file: + if self.mapping_file.endswith(".toml"): + with open(self.mapping_file, "rb") as fileobj: + file_style = ( + "pyproject.toml" + if os.path.basename(self.mapping_file) == "pyproject.toml" + else "standalone" + ) + method_map, options_map = _parse_mapping_toml( + fileobj, + filename=self.mapping_file, + style=file_style, + ) + else: + with open(self.mapping_file) as fileobj: + method_map, options_map = parse_mapping_cfg(fileobj, filename=self.mapping_file) + for path in self.input_paths: + mappings.append((path, method_map, options_map)) + + elif getattr(self.distribution, 'message_extractors', None): + message_extractors = self.distribution.message_extractors + for path, mapping in message_extractors.items(): + if isinstance(mapping, str): + method_map, options_map = parse_mapping_cfg(StringIO(mapping)) + else: + method_map, options_map = [], {} + for pattern, method, options in mapping: + method_map.append((pattern, method)) + options_map[pattern] = options or {} + mappings.append((path, method_map, options_map)) + + else: + for path in self.input_paths: + mappings.append((path, DEFAULT_MAPPING, {})) + + return mappings + + +class InitCatalog(CommandMixin): + description = 'create a new catalog based on a POT file' + user_options = [ + ('domain=', 'D', + "domain of PO file (default 'messages')"), + ('input-file=', 'i', + 'name of the input file'), + ('output-dir=', 'd', + 'path to output directory'), + ('output-file=', 'o', + "name of the output file (default " + "'//LC_MESSAGES/.po')"), + ('locale=', 'l', + 'locale for the new localized catalog'), + ('width=', 'w', + 'set output line width (default 76)'), + ('no-wrap', None, + 'do not break long message lines, longer than the output line width, ' + 'into several lines'), + ] + boolean_options = ['no-wrap'] + + def initialize_options(self): + self.output_dir = None + self.output_file = None + self.input_file = None + self.locale = None + self.domain = 'messages' + self.no_wrap = False + self.width = None + + def finalize_options(self): + if not self.input_file: + raise OptionError('you must specify the input file') + + if not self.locale: + raise OptionError('you must provide a locale for the new catalog') + try: + self._locale = Locale.parse(self.locale) + except UnknownLocaleError as e: + raise OptionError(e) from e + + if not self.output_file and not self.output_dir: + raise OptionError('you must specify the output directory') + if not self.output_file: + self.output_file = os.path.join(self.output_dir, self.locale, + 'LC_MESSAGES', f"{self.domain}.po") + + if not os.path.exists(os.path.dirname(self.output_file)): + os.makedirs(os.path.dirname(self.output_file)) + if self.no_wrap and self.width: + raise OptionError("'--no-wrap' and '--width' are mutually exclusive") + if not self.no_wrap and not self.width: + self.width = 76 + elif self.width is not None: + self.width = int(self.width) + + def run(self): + self.log.info( + 'creating catalog %s based on %s', self.output_file, self.input_file, + ) + + with open(self.input_file, 'rb') as infile: + # Although reading from the catalog template, read_po must be fed + # the locale in order to correctly calculate plurals + catalog = read_po(infile, locale=self.locale) + + catalog.locale = self._locale + catalog.revision_date = datetime.datetime.now(LOCALTZ) + catalog.fuzzy = False + + with open(self.output_file, 'wb') as outfile: + write_po(outfile, catalog, width=self.width) + + +class UpdateCatalog(CommandMixin): + description = 'update message catalogs from a POT file' + user_options = [ + ('domain=', 'D', + "domain of PO file (default 'messages')"), + ('input-file=', 'i', + 'name of the input file'), + ('output-dir=', 'd', + 'path to base directory containing the catalogs'), + ('output-file=', 'o', + "name of the output file (default " + "'//LC_MESSAGES/.po')"), + ('omit-header', None, + "do not include msgid "" entry in header"), + ('locale=', 'l', + 'locale of the catalog to compile'), + ('width=', 'w', + 'set output line width (default 76)'), + ('no-wrap', None, + 'do not break long message lines, longer than the output line width, ' + 'into several lines'), + ('ignore-obsolete=', None, + 'whether to omit obsolete messages from the output'), + ('init-missing=', None, + 'if any output files are missing, initialize them first'), + ('no-fuzzy-matching', 'N', + 'do not use fuzzy matching'), + ('update-header-comment', None, + 'update target header comment'), + ('previous', None, + 'keep previous msgids of translated messages'), + ('check=', None, + 'don\'t update the catalog, just return the status. Return code 0 ' + 'means nothing would change. Return code 1 means that the catalog ' + 'would be updated'), + ('ignore-pot-creation-date=', None, + 'ignore changes to POT-Creation-Date when updating or checking'), + ] + boolean_options = [ + 'omit-header', 'no-wrap', 'ignore-obsolete', 'init-missing', + 'no-fuzzy-matching', 'previous', 'update-header-comment', + 'check', 'ignore-pot-creation-date', + ] + + def initialize_options(self): + self.domain = 'messages' + self.input_file = None + self.output_dir = None + self.output_file = None + self.omit_header = False + self.locale = None + self.width = None + self.no_wrap = False + self.ignore_obsolete = False + self.init_missing = False + self.no_fuzzy_matching = False + self.update_header_comment = False + self.previous = False + self.check = False + self.ignore_pot_creation_date = False + + def finalize_options(self): + if not self.input_file: + raise OptionError('you must specify the input file') + if not self.output_file and not self.output_dir: + raise OptionError('you must specify the output file or directory') + if self.output_file and not self.locale: + raise OptionError('you must specify the locale') + + if self.init_missing: + if not self.locale: + raise OptionError( + 'you must specify the locale for ' + 'the init-missing option to work', + ) + + try: + self._locale = Locale.parse(self.locale) + except UnknownLocaleError as e: + raise OptionError(e) from e + else: + self._locale = None + + if self.no_wrap and self.width: + raise OptionError("'--no-wrap' and '--width' are mutually exclusive") + if not self.no_wrap and not self.width: + self.width = 76 + elif self.width is not None: + self.width = int(self.width) + if self.no_fuzzy_matching and self.previous: + self.previous = False + + def run(self): + check_status = {} + po_files = [] + if not self.output_file: + if self.locale: + po_files.append((self.locale, + os.path.join(self.output_dir, self.locale, + 'LC_MESSAGES', + f"{self.domain}.po"))) + else: + for locale in os.listdir(self.output_dir): + po_file = os.path.join(self.output_dir, locale, + 'LC_MESSAGES', + f"{self.domain}.po") + if os.path.exists(po_file): + po_files.append((locale, po_file)) + else: + po_files.append((self.locale, self.output_file)) + + if not po_files: + raise OptionError('no message catalogs found') + + domain = self.domain + if not domain: + domain = os.path.splitext(os.path.basename(self.input_file))[0] + + with open(self.input_file, 'rb') as infile: + template = read_po(infile) + + for locale, filename in po_files: + if self.init_missing and not os.path.exists(filename): + if self.check: + check_status[filename] = False + continue + self.log.info( + 'creating catalog %s based on %s', filename, self.input_file, + ) + + with open(self.input_file, 'rb') as infile: + # Although reading from the catalog template, read_po must + # be fed the locale in order to correctly calculate plurals + catalog = read_po(infile, locale=self.locale) + + catalog.locale = self._locale + catalog.revision_date = datetime.datetime.now(LOCALTZ) + catalog.fuzzy = False + + with open(filename, 'wb') as outfile: + write_po(outfile, catalog) + + self.log.info('updating catalog %s based on %s', filename, self.input_file) + with open(filename, 'rb') as infile: + catalog = read_po(infile, locale=locale, domain=domain) + + catalog.update( + template, self.no_fuzzy_matching, + update_header_comment=self.update_header_comment, + update_creation_date=not self.ignore_pot_creation_date, + ) + + tmpname = os.path.join(os.path.dirname(filename), + tempfile.gettempprefix() + + os.path.basename(filename)) + try: + with open(tmpname, 'wb') as tmpfile: + write_po(tmpfile, catalog, + omit_header=self.omit_header, + ignore_obsolete=self.ignore_obsolete, + include_previous=self.previous, width=self.width) + except Exception: + os.remove(tmpname) + raise + + if self.check: + with open(filename, "rb") as origfile: + original_catalog = read_po(origfile) + with open(tmpname, "rb") as newfile: + updated_catalog = read_po(newfile) + updated_catalog.revision_date = original_catalog.revision_date + check_status[filename] = updated_catalog.is_identical(original_catalog) + os.remove(tmpname) + continue + + try: + os.rename(tmpname, filename) + except OSError: + # We're probably on Windows, which doesn't support atomic + # renames, at least not through Python + # If the error is in fact due to a permissions problem, that + # same error is going to be raised from one of the following + # operations + os.remove(filename) + shutil.copy(tmpname, filename) + os.remove(tmpname) + + if self.check: + for filename, up_to_date in check_status.items(): + if up_to_date: + self.log.info('Catalog %s is up to date.', filename) + else: + self.log.warning('Catalog %s is out of date.', filename) + if not all(check_status.values()): + raise BaseError("Some catalogs are out of date.") + else: + self.log.info("All the catalogs are up-to-date.") + return + + +class CommandLineInterface: + """Command-line interface. + + This class provides a simple command-line interface to the message + extraction and PO file generation functionality. + """ + + usage = '%%prog %s [options] %s' + version = f'%prog {VERSION}' + commands = { + 'compile': 'compile message catalogs to MO files', + 'extract': 'extract messages from source files and generate a POT file', + 'init': 'create new message catalogs from a POT file', + 'update': 'update existing message catalogs from a POT file', + } + + command_classes = { + 'compile': CompileCatalog, + 'extract': ExtractMessages, + 'init': InitCatalog, + 'update': UpdateCatalog, + } + + log = None # Replaced on instance level + + def run(self, argv=None): + """Main entry point of the command-line interface. + + :param argv: list of arguments passed on the command-line + """ + + if argv is None: + argv = sys.argv + + self.parser = optparse.OptionParser(usage=self.usage % ('command', '[args]'), + version=self.version) + self.parser.disable_interspersed_args() + self.parser.print_help = self._help + self.parser.add_option('--list-locales', dest='list_locales', + action='store_true', + help="print all known locales and exit") + self.parser.add_option('-v', '--verbose', action='store_const', + dest='loglevel', const=logging.DEBUG, + help='print as much as possible') + self.parser.add_option('-q', '--quiet', action='store_const', + dest='loglevel', const=logging.ERROR, + help='print as little as possible') + self.parser.set_defaults(list_locales=False, loglevel=logging.INFO) + + options, args = self.parser.parse_args(argv[1:]) + + self._configure_logging(options.loglevel) + if options.list_locales: + identifiers = localedata.locale_identifiers() + id_width = max(len(identifier) for identifier in identifiers) + 1 + for identifier in sorted(identifiers): + locale = Locale.parse(identifier) + print(f"{identifier:<{id_width}} {locale.english_name}") + return 0 + + if not args: + self.parser.error('no valid command or option passed. ' + 'Try the -h/--help option for more information.') + + cmdname = args[0] + if cmdname not in self.commands: + self.parser.error(f'unknown command "{cmdname}"') + + cmdinst = self._configure_command(cmdname, args[1:]) + return cmdinst.run() + + def _configure_logging(self, loglevel): + self.log = log + self.log.setLevel(loglevel) + # Don't add a new handler for every instance initialization (#227), this + # would cause duplicated output when the CommandLineInterface as an + # normal Python class. + if self.log.handlers: + handler = self.log.handlers[0] + else: + handler = logging.StreamHandler() + self.log.addHandler(handler) + handler.setLevel(loglevel) + formatter = logging.Formatter('%(message)s') + handler.setFormatter(formatter) + + def _help(self): + print(self.parser.format_help()) + print("commands:") + cmd_width = max(8, max(len(command) for command in self.commands) + 1) + for name, description in sorted(self.commands.items()): + print(f" {name:<{cmd_width}} {description}") + + def _configure_command(self, cmdname, argv): + """ + :type cmdname: str + :type argv: list[str] + """ + cmdclass = self.command_classes[cmdname] + cmdinst = cmdclass() + if self.log: + cmdinst.log = self.log # Use our logger, not distutils'. + assert isinstance(cmdinst, CommandMixin) + cmdinst.initialize_options() + + parser = optparse.OptionParser( + usage=self.usage % (cmdname, ''), + description=self.commands[cmdname], + ) + as_args: str | None = getattr(cmdclass, "as_args", None) + for long, short, help in cmdclass.user_options: + name = long.strip("=") + default = getattr(cmdinst, name.replace("-", "_")) + strs = [f"--{name}"] + if short: + strs.append(f"-{short}") + strs.extend(cmdclass.option_aliases.get(name, ())) + choices = cmdclass.option_choices.get(name, None) + if name == as_args: + parser.usage += f"<{name}>" + elif name in cmdclass.boolean_options: + parser.add_option(*strs, action="store_true", help=help) + elif name in cmdclass.multiple_value_options: + parser.add_option(*strs, action="append", help=help, choices=choices) + else: + parser.add_option(*strs, help=help, default=default, choices=choices) + options, args = parser.parse_args(argv) + + if as_args: + setattr(options, as_args.replace('-', '_'), args) + + for key, value in vars(options).items(): + setattr(cmdinst, key, value) + + try: + cmdinst.ensure_finalized() + except OptionError as err: + parser.error(str(err)) + + return cmdinst + + +def main(): + return CommandLineInterface().run(sys.argv) + + +def parse_mapping(fileobj, filename=None): + warnings.warn( + "parse_mapping is deprecated, use parse_mapping_cfg instead", + DeprecationWarning, + stacklevel=2, + ) + return parse_mapping_cfg(fileobj, filename) + + +def parse_mapping_cfg(fileobj, filename=None): + """Parse an extraction method mapping from a file-like object. + + :param fileobj: a readable file-like object containing the configuration + text to parse + :param filename: the name of the file being parsed, for error messages + """ + extractors = {} + method_map = [] + options_map = {} + + parser = RawConfigParser() + parser.read_file(fileobj, filename) + + for section in parser.sections(): + if section == 'extractors': + extractors = dict(parser.items(section)) + else: + method, pattern = (part.strip() for part in section.split(':', 1)) + method_map.append((pattern, method)) + options_map[pattern] = dict(parser.items(section)) + + if extractors: + for idx, (pattern, method) in enumerate(method_map): + if method in extractors: + method = extractors[method] + method_map[idx] = (pattern, method) + + return method_map, options_map + + +def _parse_config_object(config: dict, *, filename="(unknown)"): + extractors = {} + method_map = [] + options_map = {} + + extractors_read = config.get("extractors", {}) + if not isinstance(extractors_read, dict): + raise ConfigurationError(f"{filename}: extractors: Expected a dictionary, got {type(extractors_read)!r}") + for method, callable_spec in extractors_read.items(): + if not isinstance(method, str): + # Impossible via TOML, but could happen with a custom object. + raise ConfigurationError(f"{filename}: extractors: Extraction method must be a string, got {method!r}") + if not isinstance(callable_spec, str): + raise ConfigurationError(f"{filename}: extractors: Callable specification must be a string, got {callable_spec!r}") + extractors[method] = callable_spec + + if "mapping" in config: + raise ConfigurationError(f"{filename}: 'mapping' is not a valid key, did you mean 'mappings'?") + + mappings_read = config.get("mappings", []) + if not isinstance(mappings_read, list): + raise ConfigurationError(f"{filename}: mappings: Expected a list, got {type(mappings_read)!r}") + for idx, entry in enumerate(mappings_read): + if not isinstance(entry, dict): + raise ConfigurationError(f"{filename}: mappings[{idx}]: Expected a dictionary, got {type(entry)!r}") + entry = entry.copy() + + method = entry.pop("method", None) + if not isinstance(method, str): + raise ConfigurationError(f"{filename}: mappings[{idx}]: 'method' must be a string, got {method!r}") + method = extractors.get(method, method) # Map the extractor name to the callable now + + pattern = entry.pop("pattern", None) + if not isinstance(pattern, (list, str)): + raise ConfigurationError(f"{filename}: mappings[{idx}]: 'pattern' must be a list or a string, got {pattern!r}") + if not isinstance(pattern, list): + pattern = [pattern] + + for pat in pattern: + if not isinstance(pat, str): + raise ConfigurationError(f"{filename}: mappings[{idx}]: 'pattern' elements must be strings, got {pat!r}") + method_map.append((pat, method)) + options_map[pat] = entry + + return method_map, options_map + + +def _parse_mapping_toml( + fileobj: BinaryIO, + filename: str = "(unknown)", + style: Literal["standalone", "pyproject.toml"] = "standalone", +): + """Parse an extraction method mapping from a binary file-like object. + + .. warning: As of this version of Babel, this is a private API subject to changes. + + :param fileobj: a readable binary file-like object containing the configuration TOML to parse + :param filename: the name of the file being parsed, for error messages + :param style: whether the file is in the style of a `pyproject.toml` file, i.e. whether to look for `tool.babel`. + """ + try: + import tomllib + except ImportError: + try: + import tomli as tomllib + except ImportError as ie: # pragma: no cover + raise ImportError("tomli or tomllib is required to parse TOML files") from ie + + try: + parsed_data = tomllib.load(fileobj) + except tomllib.TOMLDecodeError as e: + raise ConfigurationError(f"{filename}: Error parsing TOML file: {e}") from e + + if style == "pyproject.toml": + try: + babel_data = parsed_data["tool"]["babel"] + except (TypeError, KeyError) as e: + raise ConfigurationError(f"{filename}: No 'tool.babel' section found in file") from e + elif style == "standalone": + babel_data = parsed_data + if "babel" in babel_data: + raise ConfigurationError(f"{filename}: 'babel' should not be present in a stand-alone configuration file") + else: # pragma: no cover + raise ValueError(f"Unknown TOML style {style!r}") + + return _parse_config_object(babel_data, filename=filename) + + +def _parse_spec(s: str) -> tuple[int | None, tuple[int | tuple[int, str], ...]]: + inds = [] + number = None + for x in s.split(','): + if x[-1] == 't': + number = int(x[:-1]) + elif x[-1] == 'c': + inds.append((int(x[:-1]), 'c')) + else: + inds.append(int(x)) + return number, tuple(inds) + + +def parse_keywords(strings: Iterable[str] = ()): + """Parse keywords specifications from the given list of strings. + + >>> import pprint + >>> keywords = ['_', 'dgettext:2', 'dngettext:2,3', 'pgettext:1c,2', + ... 'polymorphic:1', 'polymorphic:2,2t', 'polymorphic:3c,3t'] + >>> pprint.pprint(parse_keywords(keywords)) + {'_': None, + 'dgettext': (2,), + 'dngettext': (2, 3), + 'pgettext': ((1, 'c'), 2), + 'polymorphic': {None: (1,), 2: (2,), 3: ((3, 'c'),)}} + + The input keywords are in GNU Gettext style; see :doc:`cmdline` for details. + + The output is a dictionary mapping keyword names to a dictionary of specifications. + Keys in this dictionary are numbers of arguments, where ``None`` means that all numbers + of arguments are matched, and a number means only calls with that number of arguments + are matched (which happens when using the "t" specifier). However, as a special + case for backwards compatibility, if the dictionary of specifications would + be ``{None: x}``, i.e., there is only one specification and it matches all argument + counts, then it is collapsed into just ``x``. + + A specification is either a tuple or None. If a tuple, each element can be either a number + ``n``, meaning that the nth argument should be extracted as a message, or the tuple + ``(n, 'c')``, meaning that the nth argument should be extracted as context for the + messages. A ``None`` specification is equivalent to ``(1,)``, extracting the first + argument. + """ + keywords = {} + for string in strings: + if ':' in string: + funcname, spec_str = string.split(':') + number, spec = _parse_spec(spec_str) + else: + funcname = string + number = None + spec = None + keywords.setdefault(funcname, {})[number] = spec + + # For best backwards compatibility, collapse {None: x} into x. + for k, v in keywords.items(): + if set(v) == {None}: + keywords[k] = v[None] + + return keywords + + +def __getattr__(name: str): + # Re-exports for backwards compatibility; + # `setuptools_frontend` is the canonical import location. + if name in {'check_message_extractors', 'compile_catalog', 'extract_messages', 'init_catalog', 'update_catalog'}: + from babel.messages import setuptools_frontend + + return getattr(setuptools_frontend, name) + + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + + +if __name__ == '__main__': + main() diff --git a/venv/lib/python3.12/site-packages/babel/messages/jslexer.py b/venv/lib/python3.12/site-packages/babel/messages/jslexer.py new file mode 100644 index 00000000..5fc4956f --- /dev/null +++ b/venv/lib/python3.12/site-packages/babel/messages/jslexer.py @@ -0,0 +1,204 @@ +""" + babel.messages.jslexer + ~~~~~~~~~~~~~~~~~~~~~~ + + A simple JavaScript 1.5 lexer which is used for the JavaScript + extractor. + + :copyright: (c) 2013-2025 by the Babel Team. + :license: BSD, see LICENSE for more details. +""" +from __future__ import annotations + +import re +from collections.abc import Generator +from typing import NamedTuple + +operators: list[str] = sorted([ + '+', '-', '*', '%', '!=', '==', '<', '>', '<=', '>=', '=', + '+=', '-=', '*=', '%=', '<<', '>>', '>>>', '<<=', '>>=', + '>>>=', '&', '&=', '|', '|=', '&&', '||', '^', '^=', '(', ')', + '[', ']', '{', '}', '!', '--', '++', '~', ',', ';', '.', ':', +], key=len, reverse=True) + +escapes: dict[str, str] = {'b': '\b', 'f': '\f', 'n': '\n', 'r': '\r', 't': '\t'} + +name_re = re.compile(r'[\w$_][\w\d$_]*', re.UNICODE) +dotted_name_re = re.compile(r'[\w$_][\w\d$_.]*[\w\d$_.]', re.UNICODE) +division_re = re.compile(r'/=?') +regex_re = re.compile(r'/(?:[^/\\]*(?:\\.[^/\\]*)*)/[a-zA-Z]*', re.DOTALL) +line_re = re.compile(r'(\r\n|\n|\r)') +line_join_re = re.compile(r'\\' + line_re.pattern) +uni_escape_re = re.compile(r'[a-fA-F0-9]{1,4}') +hex_escape_re = re.compile(r'[a-fA-F0-9]{1,2}') + + +class Token(NamedTuple): + type: str + value: str + lineno: int + + +_rules: list[tuple[str | None, re.Pattern[str]]] = [ + (None, re.compile(r'\s+', re.UNICODE)), + (None, re.compile(r' Use Coherence + if chaos_difference < 0.01 and coherence_difference > 0.02: + return self.coherence > other.coherence + elif chaos_difference < 0.01 and coherence_difference <= 0.02: + # When having a difficult decision, use the result that decoded as many multi-byte as possible. + # preserve RAM usage! + if len(self._payload) >= TOO_BIG_SEQUENCE: + return self.chaos < other.chaos + return self.multi_byte_usage > other.multi_byte_usage + + return self.chaos < other.chaos + + @property + def multi_byte_usage(self) -> float: + return 1.0 - (len(str(self)) / len(self.raw)) + + def __str__(self) -> str: + # Lazy Str Loading + if self._string is None: + self._string = str(self._payload, self._encoding, "strict") + return self._string + + def __repr__(self) -> str: + return f"" + + def add_submatch(self, other: CharsetMatch) -> None: + if not isinstance(other, CharsetMatch) or other == self: + raise ValueError( + "Unable to add instance <{}> as a submatch of a CharsetMatch".format( + other.__class__ + ) + ) + + other._string = None # Unload RAM usage; dirty trick. + self._leaves.append(other) + + @property + def encoding(self) -> str: + return self._encoding + + @property + def encoding_aliases(self) -> list[str]: + """ + Encoding name are known by many name, using this could help when searching for IBM855 when it's listed as CP855. + """ + also_known_as: list[str] = [] + for u, p in aliases.items(): + if self.encoding == u: + also_known_as.append(p) + elif self.encoding == p: + also_known_as.append(u) + return also_known_as + + @property + def bom(self) -> bool: + return self._has_sig_or_bom + + @property + def byte_order_mark(self) -> bool: + return self._has_sig_or_bom + + @property + def languages(self) -> list[str]: + """ + Return the complete list of possible languages found in decoded sequence. + Usually not really useful. Returned list may be empty even if 'language' property return something != 'Unknown'. + """ + return [e[0] for e in self._languages] + + @property + def language(self) -> str: + """ + Most probable language found in decoded sequence. If none were detected or inferred, the property will return + "Unknown". + """ + if not self._languages: + # Trying to infer the language based on the given encoding + # Its either English or we should not pronounce ourselves in certain cases. + if "ascii" in self.could_be_from_charset: + return "English" + + # doing it there to avoid circular import + from charset_normalizer.cd import encoding_languages, mb_encoding_languages + + languages = ( + mb_encoding_languages(self.encoding) + if is_multi_byte_encoding(self.encoding) + else encoding_languages(self.encoding) + ) + + if len(languages) == 0 or "Latin Based" in languages: + return "Unknown" + + return languages[0] + + return self._languages[0][0] + + @property + def chaos(self) -> float: + return self._mean_mess_ratio + + @property + def coherence(self) -> float: + if not self._languages: + return 0.0 + return self._languages[0][1] + + @property + def percent_chaos(self) -> float: + return round(self.chaos * 100, ndigits=3) + + @property + def percent_coherence(self) -> float: + return round(self.coherence * 100, ndigits=3) + + @property + def raw(self) -> bytes: + """ + Original untouched bytes. + """ + return self._payload + + @property + def submatch(self) -> list[CharsetMatch]: + return self._leaves + + @property + def has_submatch(self) -> bool: + return len(self._leaves) > 0 + + @property + def alphabets(self) -> list[str]: + if self._unicode_ranges is not None: + return self._unicode_ranges + # list detected ranges + detected_ranges: list[str | None] = [unicode_range(char) for char in str(self)] + # filter and sort + self._unicode_ranges = sorted(list({r for r in detected_ranges if r})) + return self._unicode_ranges + + @property + def could_be_from_charset(self) -> list[str]: + """ + The complete list of encoding that output the exact SAME str result and therefore could be the originating + encoding. + This list does include the encoding available in property 'encoding'. + """ + return [self._encoding] + [m.encoding for m in self._leaves] + + def output(self, encoding: str = "utf_8") -> bytes: + """ + Method to get re-encoded bytes payload using given target encoding. Default to UTF-8. + Any errors will be simply ignored by the encoder NOT replaced. + """ + if self._output_encoding is None or self._output_encoding != encoding: + self._output_encoding = encoding + decoded_string = str(self) + if ( + self._preemptive_declaration is not None + and self._preemptive_declaration.lower() + not in ["utf-8", "utf8", "utf_8"] + ): + patched_header = sub( + RE_POSSIBLE_ENCODING_INDICATION, + lambda m: m.string[m.span()[0] : m.span()[1]].replace( + m.groups()[0], + iana_name(self._output_encoding).replace("_", "-"), # type: ignore[arg-type] + ), + decoded_string[:8192], + count=1, + ) + + decoded_string = patched_header + decoded_string[8192:] + + self._output_payload = decoded_string.encode(encoding, "replace") + + return self._output_payload # type: ignore + + @property + def fingerprint(self) -> str: + """ + Retrieve the unique SHA256 computed using the transformed (re-encoded) payload. Not the original one. + """ + return sha256(self.output()).hexdigest() + + +class CharsetMatches: + """ + Container with every CharsetMatch items ordered by default from most probable to the less one. + Act like a list(iterable) but does not implements all related methods. + """ + + def __init__(self, results: list[CharsetMatch] | None = None): + self._results: list[CharsetMatch] = sorted(results) if results else [] + + def __iter__(self) -> Iterator[CharsetMatch]: + yield from self._results + + def __getitem__(self, item: int | str) -> CharsetMatch: + """ + Retrieve a single item either by its position or encoding name (alias may be used here). + Raise KeyError upon invalid index or encoding not present in results. + """ + if isinstance(item, int): + return self._results[item] + if isinstance(item, str): + item = iana_name(item, False) + for result in self._results: + if item in result.could_be_from_charset: + return result + raise KeyError + + def __len__(self) -> int: + return len(self._results) + + def __bool__(self) -> bool: + return len(self._results) > 0 + + def append(self, item: CharsetMatch) -> None: + """ + Insert a single match. Will be inserted accordingly to preserve sort. + Can be inserted as a submatch. + """ + if not isinstance(item, CharsetMatch): + raise ValueError( + "Cannot append instance '{}' to CharsetMatches".format( + str(item.__class__) + ) + ) + # We should disable the submatch factoring when the input file is too heavy (conserve RAM usage) + if len(item.raw) < TOO_BIG_SEQUENCE: + for match in self._results: + if match.fingerprint == item.fingerprint and match.chaos == item.chaos: + match.add_submatch(item) + return + self._results.append(item) + self._results = sorted(self._results) + + def best(self) -> CharsetMatch | None: + """ + Simply return the first match. Strict equivalent to matches[0]. + """ + if not self._results: + return None + return self._results[0] + + def first(self) -> CharsetMatch | None: + """ + Redundant method, call the method best(). Kept for BC reasons. + """ + return self.best() + + +CoherenceMatch = Tuple[str, float] +CoherenceMatches = List[CoherenceMatch] + + +class CliDetectionResult: + def __init__( + self, + path: str, + encoding: str | None, + encoding_aliases: list[str], + alternative_encodings: list[str], + language: str, + alphabets: list[str], + has_sig_or_bom: bool, + chaos: float, + coherence: float, + unicode_path: str | None, + is_preferred: bool, + ): + self.path: str = path + self.unicode_path: str | None = unicode_path + self.encoding: str | None = encoding + self.encoding_aliases: list[str] = encoding_aliases + self.alternative_encodings: list[str] = alternative_encodings + self.language: str = language + self.alphabets: list[str] = alphabets + self.has_sig_or_bom: bool = has_sig_or_bom + self.chaos: float = chaos + self.coherence: float = coherence + self.is_preferred: bool = is_preferred + + @property + def __dict__(self) -> dict[str, Any]: # type: ignore + return { + "path": self.path, + "encoding": self.encoding, + "encoding_aliases": self.encoding_aliases, + "alternative_encodings": self.alternative_encodings, + "language": self.language, + "alphabets": self.alphabets, + "has_sig_or_bom": self.has_sig_or_bom, + "chaos": self.chaos, + "coherence": self.coherence, + "unicode_path": self.unicode_path, + "is_preferred": self.is_preferred, + } + + def to_json(self) -> str: + return dumps(self.__dict__, ensure_ascii=True, indent=4) diff --git a/venv/lib/python3.12/site-packages/charset_normalizer/py.typed b/venv/lib/python3.12/site-packages/charset_normalizer/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/venv/lib/python3.12/site-packages/charset_normalizer/utils.py b/venv/lib/python3.12/site-packages/charset_normalizer/utils.py new file mode 100644 index 00000000..6bf0384c --- /dev/null +++ b/venv/lib/python3.12/site-packages/charset_normalizer/utils.py @@ -0,0 +1,414 @@ +from __future__ import annotations + +import importlib +import logging +import unicodedata +from codecs import IncrementalDecoder +from encodings.aliases import aliases +from functools import lru_cache +from re import findall +from typing import Generator + +from _multibytecodec import ( # type: ignore[import-not-found,import] + MultibyteIncrementalDecoder, +) + +from .constant import ( + ENCODING_MARKS, + IANA_SUPPORTED_SIMILAR, + RE_POSSIBLE_ENCODING_INDICATION, + UNICODE_RANGES_COMBINED, + UNICODE_SECONDARY_RANGE_KEYWORD, + UTF8_MAXIMAL_ALLOCATION, + COMMON_CJK_CHARACTERS, +) + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_accentuated(character: str) -> bool: + try: + description: str = unicodedata.name(character) + except ValueError: # Defensive: unicode database outdated? + return False + return ( + "WITH GRAVE" in description + or "WITH ACUTE" in description + or "WITH CEDILLA" in description + or "WITH DIAERESIS" in description + or "WITH CIRCUMFLEX" in description + or "WITH TILDE" in description + or "WITH MACRON" in description + or "WITH RING ABOVE" in description + ) + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def remove_accent(character: str) -> str: + decomposed: str = unicodedata.decomposition(character) + if not decomposed: + return character + + codes: list[str] = decomposed.split(" ") + + return chr(int(codes[0], 16)) + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def unicode_range(character: str) -> str | None: + """ + Retrieve the Unicode range official name from a single character. + """ + character_ord: int = ord(character) + + for range_name, ord_range in UNICODE_RANGES_COMBINED.items(): + if character_ord in ord_range: + return range_name + + return None + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_latin(character: str) -> bool: + try: + description: str = unicodedata.name(character) + except ValueError: # Defensive: unicode database outdated? + return False + return "LATIN" in description + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_punctuation(character: str) -> bool: + character_category: str = unicodedata.category(character) + + if "P" in character_category: + return True + + character_range: str | None = unicode_range(character) + + if character_range is None: + return False + + return "Punctuation" in character_range + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_symbol(character: str) -> bool: + character_category: str = unicodedata.category(character) + + if "S" in character_category or "N" in character_category: + return True + + character_range: str | None = unicode_range(character) + + if character_range is None: + return False + + return "Forms" in character_range and character_category != "Lo" + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_emoticon(character: str) -> bool: + character_range: str | None = unicode_range(character) + + if character_range is None: + return False + + return "Emoticons" in character_range or "Pictographs" in character_range + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_separator(character: str) -> bool: + if character.isspace() or character in {"|", "+", "<", ">"}: + return True + + character_category: str = unicodedata.category(character) + + return "Z" in character_category or character_category in {"Po", "Pd", "Pc"} + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_case_variable(character: str) -> bool: + return character.islower() != character.isupper() + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_cjk(character: str) -> bool: + try: + character_name = unicodedata.name(character) + except ValueError: # Defensive: unicode database outdated? + return False + + return "CJK" in character_name + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_hiragana(character: str) -> bool: + try: + character_name = unicodedata.name(character) + except ValueError: # Defensive: unicode database outdated? + return False + + return "HIRAGANA" in character_name + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_katakana(character: str) -> bool: + try: + character_name = unicodedata.name(character) + except ValueError: # Defensive: unicode database outdated? + return False + + return "KATAKANA" in character_name + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_hangul(character: str) -> bool: + try: + character_name = unicodedata.name(character) + except ValueError: # Defensive: unicode database outdated? + return False + + return "HANGUL" in character_name + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_thai(character: str) -> bool: + try: + character_name = unicodedata.name(character) + except ValueError: # Defensive: unicode database outdated? + return False + + return "THAI" in character_name + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_arabic(character: str) -> bool: + try: + character_name = unicodedata.name(character) + except ValueError: # Defensive: unicode database outdated? + return False + + return "ARABIC" in character_name + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_arabic_isolated_form(character: str) -> bool: + try: + character_name = unicodedata.name(character) + except ValueError: # Defensive: unicode database outdated? + return False + + return "ARABIC" in character_name and "ISOLATED FORM" in character_name + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_cjk_uncommon(character: str) -> bool: + return character not in COMMON_CJK_CHARACTERS + + +@lru_cache(maxsize=len(UNICODE_RANGES_COMBINED)) +def is_unicode_range_secondary(range_name: str) -> bool: + return any(keyword in range_name for keyword in UNICODE_SECONDARY_RANGE_KEYWORD) + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_unprintable(character: str) -> bool: + return ( + character.isspace() is False # includes \n \t \r \v + and character.isprintable() is False + and character != "\x1a" # Why? Its the ASCII substitute character. + and character != "\ufeff" # bug discovered in Python, + # Zero Width No-Break Space located in Arabic Presentation Forms-B, Unicode 1.1 not acknowledged as space. + ) + + +def any_specified_encoding(sequence: bytes, search_zone: int = 8192) -> str | None: + """ + Extract using ASCII-only decoder any specified encoding in the first n-bytes. + """ + if not isinstance(sequence, bytes): + raise TypeError + + seq_len: int = len(sequence) + + results: list[str] = findall( + RE_POSSIBLE_ENCODING_INDICATION, + sequence[: min(seq_len, search_zone)].decode("ascii", errors="ignore"), + ) + + if len(results) == 0: + return None + + for specified_encoding in results: + specified_encoding = specified_encoding.lower().replace("-", "_") + + encoding_alias: str + encoding_iana: str + + for encoding_alias, encoding_iana in aliases.items(): + if encoding_alias == specified_encoding: + return encoding_iana + if encoding_iana == specified_encoding: + return encoding_iana + + return None + + +@lru_cache(maxsize=128) +def is_multi_byte_encoding(name: str) -> bool: + """ + Verify is a specific encoding is a multi byte one based on it IANA name + """ + return name in { + "utf_8", + "utf_8_sig", + "utf_16", + "utf_16_be", + "utf_16_le", + "utf_32", + "utf_32_le", + "utf_32_be", + "utf_7", + } or issubclass( + importlib.import_module(f"encodings.{name}").IncrementalDecoder, + MultibyteIncrementalDecoder, + ) + + +def identify_sig_or_bom(sequence: bytes) -> tuple[str | None, bytes]: + """ + Identify and extract SIG/BOM in given sequence. + """ + + for iana_encoding in ENCODING_MARKS: + marks: bytes | list[bytes] = ENCODING_MARKS[iana_encoding] + + if isinstance(marks, bytes): + marks = [marks] + + for mark in marks: + if sequence.startswith(mark): + return iana_encoding, mark + + return None, b"" + + +def should_strip_sig_or_bom(iana_encoding: str) -> bool: + return iana_encoding not in {"utf_16", "utf_32"} + + +def iana_name(cp_name: str, strict: bool = True) -> str: + """Returns the Python normalized encoding name (Not the IANA official name).""" + cp_name = cp_name.lower().replace("-", "_") + + encoding_alias: str + encoding_iana: str + + for encoding_alias, encoding_iana in aliases.items(): + if cp_name in [encoding_alias, encoding_iana]: + return encoding_iana + + if strict: + raise ValueError(f"Unable to retrieve IANA for '{cp_name}'") + + return cp_name + + +def cp_similarity(iana_name_a: str, iana_name_b: str) -> float: + if is_multi_byte_encoding(iana_name_a) or is_multi_byte_encoding(iana_name_b): + return 0.0 + + decoder_a = importlib.import_module(f"encodings.{iana_name_a}").IncrementalDecoder + decoder_b = importlib.import_module(f"encodings.{iana_name_b}").IncrementalDecoder + + id_a: IncrementalDecoder = decoder_a(errors="ignore") + id_b: IncrementalDecoder = decoder_b(errors="ignore") + + character_match_count: int = 0 + + for i in range(255): + to_be_decoded: bytes = bytes([i]) + if id_a.decode(to_be_decoded) == id_b.decode(to_be_decoded): + character_match_count += 1 + + return character_match_count / 254 + + +def is_cp_similar(iana_name_a: str, iana_name_b: str) -> bool: + """ + Determine if two code page are at least 80% similar. IANA_SUPPORTED_SIMILAR dict was generated using + the function cp_similarity. + """ + return ( + iana_name_a in IANA_SUPPORTED_SIMILAR + and iana_name_b in IANA_SUPPORTED_SIMILAR[iana_name_a] + ) + + +def set_logging_handler( + name: str = "charset_normalizer", + level: int = logging.INFO, + format_string: str = "%(asctime)s | %(levelname)s | %(message)s", +) -> None: + logger = logging.getLogger(name) + logger.setLevel(level) + + handler = logging.StreamHandler() + handler.setFormatter(logging.Formatter(format_string)) + logger.addHandler(handler) + + +def cut_sequence_chunks( + sequences: bytes, + encoding_iana: str, + offsets: range, + chunk_size: int, + bom_or_sig_available: bool, + strip_sig_or_bom: bool, + sig_payload: bytes, + is_multi_byte_decoder: bool, + decoded_payload: str | None = None, +) -> Generator[str, None, None]: + if decoded_payload and is_multi_byte_decoder is False: + for i in offsets: + chunk = decoded_payload[i : i + chunk_size] + if not chunk: + break + yield chunk + else: + for i in offsets: + chunk_end = i + chunk_size + if chunk_end > len(sequences) + 8: + continue + + cut_sequence = sequences[i : i + chunk_size] + + if bom_or_sig_available and strip_sig_or_bom is False: + cut_sequence = sig_payload + cut_sequence + + chunk = cut_sequence.decode( + encoding_iana, + errors="ignore" if is_multi_byte_decoder else "strict", + ) + + # multi-byte bad cutting detector and adjustment + # not the cleanest way to perform that fix but clever enough for now. + if is_multi_byte_decoder and i > 0: + chunk_partial_size_chk: int = min(chunk_size, 16) + + if ( + decoded_payload + and chunk[:chunk_partial_size_chk] not in decoded_payload + ): + for j in range(i, i - 4, -1): + cut_sequence = sequences[j:chunk_end] + + if bom_or_sig_available and strip_sig_or_bom is False: + cut_sequence = sig_payload + cut_sequence + + chunk = cut_sequence.decode(encoding_iana, errors="ignore") + + if chunk[:chunk_partial_size_chk] in decoded_payload: + break + + yield chunk diff --git a/venv/lib/python3.12/site-packages/charset_normalizer/version.py b/venv/lib/python3.12/site-packages/charset_normalizer/version.py new file mode 100644 index 00000000..e5687e3c --- /dev/null +++ b/venv/lib/python3.12/site-packages/charset_normalizer/version.py @@ -0,0 +1,8 @@ +""" +Expose version +""" + +from __future__ import annotations + +__version__ = "3.4.2" +VERSION = __version__.split(".") diff --git a/venv/lib/python3.12/site-packages/click-8.2.1.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/click-8.2.1.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/click-8.2.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.12/site-packages/click-8.2.1.dist-info/METADATA b/venv/lib/python3.12/site-packages/click-8.2.1.dist-info/METADATA new file mode 100644 index 00000000..e6c05af4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/click-8.2.1.dist-info/METADATA @@ -0,0 +1,82 @@ +Metadata-Version: 2.4 +Name: click +Version: 8.2.1 +Summary: Composable command line interface toolkit +Maintainer-email: Pallets +Requires-Python: >=3.10 +Description-Content-Type: text/markdown +License-Expression: BSD-3-Clause +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Typing :: Typed +License-File: LICENSE.txt +Requires-Dist: colorama; platform_system == 'Windows' +Project-URL: Changes, https://click.palletsprojects.com/page/changes/ +Project-URL: Chat, https://discord.gg/pallets +Project-URL: Documentation, https://click.palletsprojects.com/ +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Source, https://github.com/pallets/click/ + +# $ click_ + +Click is a Python package for creating beautiful command line interfaces +in a composable way with as little code as necessary. It's the "Command +Line Interface Creation Kit". It's highly configurable but comes with +sensible defaults out of the box. + +It aims to make the process of writing command line tools quick and fun +while also preventing any frustration caused by the inability to +implement an intended CLI API. + +Click in three points: + +- Arbitrary nesting of commands +- Automatic help page generation +- Supports lazy loading of subcommands at runtime + + +## A Simple Example + +```python +import click + +@click.command() +@click.option("--count", default=1, help="Number of greetings.") +@click.option("--name", prompt="Your name", help="The person to greet.") +def hello(count, name): + """Simple program that greets NAME for a total of COUNT times.""" + for _ in range(count): + click.echo(f"Hello, {name}!") + +if __name__ == '__main__': + hello() +``` + +``` +$ python hello.py --count=3 +Your name: Click +Hello, Click! +Hello, Click! +Hello, Click! +``` + + +## Donate + +The Pallets organization develops and supports Click and other popular +packages. In order to grow the community of contributors and users, and +allow the maintainers to devote more time to the projects, [please +donate today][]. + +[please donate today]: https://palletsprojects.com/donate + +## Contributing + +See our [detailed contributing documentation][contrib] for many ways to +contribute, including reporting issues, requesting features, asking or answering +questions, and making PRs. + +[contrib]: https://palletsprojects.com/contributing/ + diff --git a/venv/lib/python3.12/site-packages/click-8.2.1.dist-info/RECORD b/venv/lib/python3.12/site-packages/click-8.2.1.dist-info/RECORD new file mode 100644 index 00000000..61f4cd2b --- /dev/null +++ b/venv/lib/python3.12/site-packages/click-8.2.1.dist-info/RECORD @@ -0,0 +1,38 @@ +click-8.2.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +click-8.2.1.dist-info/METADATA,sha256=dI1MbhHTLoKD2tNCCGnx9rK2gok23HDNylFeLKdLSik,2471 +click-8.2.1.dist-info/RECORD,, +click-8.2.1.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82 +click-8.2.1.dist-info/licenses/LICENSE.txt,sha256=morRBqOU6FO_4h9C9OctWSgZoigF2ZG18ydQKSkrZY0,1475 +click/__init__.py,sha256=6YyS1aeyknZ0LYweWozNZy0A9nZ_11wmYIhv3cbQrYo,4473 +click/__pycache__/__init__.cpython-312.pyc,, +click/__pycache__/_compat.cpython-312.pyc,, +click/__pycache__/_termui_impl.cpython-312.pyc,, +click/__pycache__/_textwrap.cpython-312.pyc,, +click/__pycache__/_winconsole.cpython-312.pyc,, +click/__pycache__/core.cpython-312.pyc,, +click/__pycache__/decorators.cpython-312.pyc,, +click/__pycache__/exceptions.cpython-312.pyc,, +click/__pycache__/formatting.cpython-312.pyc,, +click/__pycache__/globals.cpython-312.pyc,, +click/__pycache__/parser.cpython-312.pyc,, +click/__pycache__/shell_completion.cpython-312.pyc,, +click/__pycache__/termui.cpython-312.pyc,, +click/__pycache__/testing.cpython-312.pyc,, +click/__pycache__/types.cpython-312.pyc,, +click/__pycache__/utils.cpython-312.pyc,, +click/_compat.py,sha256=v3xBZkFbvA1BXPRkFfBJc6-pIwPI7345m-kQEnpVAs4,18693 +click/_termui_impl.py,sha256=ASXhLi9IQIc0Js9KQSS-3-SLZcPet3VqysBf9WgbbpI,26712 +click/_textwrap.py,sha256=BOae0RQ6vg3FkNgSJyOoGzG1meGMxJ_ukWVZKx_v-0o,1400 +click/_winconsole.py,sha256=_vxUuUaxwBhoR0vUWCNuHY8VUefiMdCIyU2SXPqoF-A,8465 +click/core.py,sha256=gUhpNS9cFBGdEXXdisGVG-eRvGf49RTyFagxulqwdFw,117343 +click/decorators.py,sha256=5P7abhJtAQYp_KHgjUvhMv464ERwOzrv2enNknlwHyQ,18461 +click/exceptions.py,sha256=1rdtXgHJ1b3OjGkN-UpXB9t_HCBihJvh_DtpmLmwn9s,9891 +click/formatting.py,sha256=Bhqx4QXdKQ9W4WKknIwj5KPKFmtduGOuGq1yw_THLZ8,9726 +click/globals.py,sha256=gM-Nh6A4M0HB_SgkaF5M4ncGGMDHc_flHXu9_oh4GEU,1923 +click/parser.py,sha256=nU1Ah2p11q29ul1vNdU9swPo_PUuKrxU6YXToi71q1c,18979 +click/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +click/shell_completion.py,sha256=CQSGdjgun4ORbOZrXP0CVhEtPx4knsufOkRsDiK64cM,19857 +click/termui.py,sha256=vAYrKC2a7f_NfEIhAThEVYfa__ib5XQbTSCGtJlABRA,30847 +click/testing.py,sha256=2eLdAaCJCGToP5Tw-XN8JjrDb3wbJIfARxg3d0crW5M,18702 +click/types.py,sha256=KBTRxN28cR1VZ5mb9iJX98MQSw_p9SGzljqfEI8z5Tw,38389 +click/utils.py,sha256=b1Mm-usEDBHtEwcPltPIn3zSK4nw2KTp5GC7_oSTlLo,20245 diff --git a/venv/lib/python3.12/site-packages/click-8.2.1.dist-info/WHEEL b/venv/lib/python3.12/site-packages/click-8.2.1.dist-info/WHEEL new file mode 100644 index 00000000..d8b9936d --- /dev/null +++ b/venv/lib/python3.12/site-packages/click-8.2.1.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: flit 3.12.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/venv/lib/python3.12/site-packages/click-8.2.1.dist-info/licenses/LICENSE.txt b/venv/lib/python3.12/site-packages/click-8.2.1.dist-info/licenses/LICENSE.txt new file mode 100644 index 00000000..d12a8491 --- /dev/null +++ b/venv/lib/python3.12/site-packages/click-8.2.1.dist-info/licenses/LICENSE.txt @@ -0,0 +1,28 @@ +Copyright 2014 Pallets + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/lib/python3.12/site-packages/click/__init__.py b/venv/lib/python3.12/site-packages/click/__init__.py new file mode 100644 index 00000000..1aa547c5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/click/__init__.py @@ -0,0 +1,123 @@ +""" +Click is a simple Python module inspired by the stdlib optparse to make +writing command line scripts fun. Unlike other modules, it's based +around a simple API that does not come with too much magic and is +composable. +""" + +from __future__ import annotations + +from .core import Argument as Argument +from .core import Command as Command +from .core import CommandCollection as CommandCollection +from .core import Context as Context +from .core import Group as Group +from .core import Option as Option +from .core import Parameter as Parameter +from .decorators import argument as argument +from .decorators import command as command +from .decorators import confirmation_option as confirmation_option +from .decorators import group as group +from .decorators import help_option as help_option +from .decorators import make_pass_decorator as make_pass_decorator +from .decorators import option as option +from .decorators import pass_context as pass_context +from .decorators import pass_obj as pass_obj +from .decorators import password_option as password_option +from .decorators import version_option as version_option +from .exceptions import Abort as Abort +from .exceptions import BadArgumentUsage as BadArgumentUsage +from .exceptions import BadOptionUsage as BadOptionUsage +from .exceptions import BadParameter as BadParameter +from .exceptions import ClickException as ClickException +from .exceptions import FileError as FileError +from .exceptions import MissingParameter as MissingParameter +from .exceptions import NoSuchOption as NoSuchOption +from .exceptions import UsageError as UsageError +from .formatting import HelpFormatter as HelpFormatter +from .formatting import wrap_text as wrap_text +from .globals import get_current_context as get_current_context +from .termui import clear as clear +from .termui import confirm as confirm +from .termui import echo_via_pager as echo_via_pager +from .termui import edit as edit +from .termui import getchar as getchar +from .termui import launch as launch +from .termui import pause as pause +from .termui import progressbar as progressbar +from .termui import prompt as prompt +from .termui import secho as secho +from .termui import style as style +from .termui import unstyle as unstyle +from .types import BOOL as BOOL +from .types import Choice as Choice +from .types import DateTime as DateTime +from .types import File as File +from .types import FLOAT as FLOAT +from .types import FloatRange as FloatRange +from .types import INT as INT +from .types import IntRange as IntRange +from .types import ParamType as ParamType +from .types import Path as Path +from .types import STRING as STRING +from .types import Tuple as Tuple +from .types import UNPROCESSED as UNPROCESSED +from .types import UUID as UUID +from .utils import echo as echo +from .utils import format_filename as format_filename +from .utils import get_app_dir as get_app_dir +from .utils import get_binary_stream as get_binary_stream +from .utils import get_text_stream as get_text_stream +from .utils import open_file as open_file + + +def __getattr__(name: str) -> object: + import warnings + + if name == "BaseCommand": + from .core import _BaseCommand + + warnings.warn( + "'BaseCommand' is deprecated and will be removed in Click 9.0. Use" + " 'Command' instead.", + DeprecationWarning, + stacklevel=2, + ) + return _BaseCommand + + if name == "MultiCommand": + from .core import _MultiCommand + + warnings.warn( + "'MultiCommand' is deprecated and will be removed in Click 9.0. Use" + " 'Group' instead.", + DeprecationWarning, + stacklevel=2, + ) + return _MultiCommand + + if name == "OptionParser": + from .parser import _OptionParser + + warnings.warn( + "'OptionParser' is deprecated and will be removed in Click 9.0. The" + " old parser is available in 'optparse'.", + DeprecationWarning, + stacklevel=2, + ) + return _OptionParser + + if name == "__version__": + import importlib.metadata + import warnings + + warnings.warn( + "The '__version__' attribute is deprecated and will be removed in" + " Click 9.1. Use feature detection or" + " 'importlib.metadata.version(\"click\")' instead.", + DeprecationWarning, + stacklevel=2, + ) + return importlib.metadata.version("click") + + raise AttributeError(name) diff --git a/venv/lib/python3.12/site-packages/click/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/click/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 00000000..51dd1727 Binary files /dev/null and b/venv/lib/python3.12/site-packages/click/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/click/__pycache__/_compat.cpython-312.pyc b/venv/lib/python3.12/site-packages/click/__pycache__/_compat.cpython-312.pyc new file mode 100644 index 00000000..7aa099e6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/click/__pycache__/_compat.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/click/__pycache__/_termui_impl.cpython-312.pyc b/venv/lib/python3.12/site-packages/click/__pycache__/_termui_impl.cpython-312.pyc new file mode 100644 index 00000000..b0f9292e Binary files /dev/null and b/venv/lib/python3.12/site-packages/click/__pycache__/_termui_impl.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/click/__pycache__/_textwrap.cpython-312.pyc b/venv/lib/python3.12/site-packages/click/__pycache__/_textwrap.cpython-312.pyc new file mode 100644 index 00000000..b2273012 Binary files /dev/null and b/venv/lib/python3.12/site-packages/click/__pycache__/_textwrap.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/click/__pycache__/_winconsole.cpython-312.pyc b/venv/lib/python3.12/site-packages/click/__pycache__/_winconsole.cpython-312.pyc new file mode 100644 index 00000000..08c78659 Binary files /dev/null and b/venv/lib/python3.12/site-packages/click/__pycache__/_winconsole.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/click/__pycache__/core.cpython-312.pyc b/venv/lib/python3.12/site-packages/click/__pycache__/core.cpython-312.pyc new file mode 100644 index 00000000..970e292d Binary files /dev/null and b/venv/lib/python3.12/site-packages/click/__pycache__/core.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/click/__pycache__/decorators.cpython-312.pyc b/venv/lib/python3.12/site-packages/click/__pycache__/decorators.cpython-312.pyc new file mode 100644 index 00000000..33bc5ce1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/click/__pycache__/decorators.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/click/__pycache__/exceptions.cpython-312.pyc b/venv/lib/python3.12/site-packages/click/__pycache__/exceptions.cpython-312.pyc new file mode 100644 index 00000000..6c8bccd2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/click/__pycache__/exceptions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/click/__pycache__/formatting.cpython-312.pyc b/venv/lib/python3.12/site-packages/click/__pycache__/formatting.cpython-312.pyc new file mode 100644 index 00000000..32c5a3cb Binary files /dev/null and b/venv/lib/python3.12/site-packages/click/__pycache__/formatting.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/click/__pycache__/globals.cpython-312.pyc b/venv/lib/python3.12/site-packages/click/__pycache__/globals.cpython-312.pyc new file mode 100644 index 00000000..28c1941b Binary files /dev/null and b/venv/lib/python3.12/site-packages/click/__pycache__/globals.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/click/__pycache__/parser.cpython-312.pyc b/venv/lib/python3.12/site-packages/click/__pycache__/parser.cpython-312.pyc new file mode 100644 index 00000000..072c5f7f Binary files /dev/null and b/venv/lib/python3.12/site-packages/click/__pycache__/parser.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/click/__pycache__/shell_completion.cpython-312.pyc b/venv/lib/python3.12/site-packages/click/__pycache__/shell_completion.cpython-312.pyc new file mode 100644 index 00000000..4969ab53 Binary files /dev/null and b/venv/lib/python3.12/site-packages/click/__pycache__/shell_completion.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/click/__pycache__/termui.cpython-312.pyc b/venv/lib/python3.12/site-packages/click/__pycache__/termui.cpython-312.pyc new file mode 100644 index 00000000..cdc31f77 Binary files /dev/null and b/venv/lib/python3.12/site-packages/click/__pycache__/termui.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/click/__pycache__/testing.cpython-312.pyc b/venv/lib/python3.12/site-packages/click/__pycache__/testing.cpython-312.pyc new file mode 100644 index 00000000..82e747db Binary files /dev/null and b/venv/lib/python3.12/site-packages/click/__pycache__/testing.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/click/__pycache__/types.cpython-312.pyc b/venv/lib/python3.12/site-packages/click/__pycache__/types.cpython-312.pyc new file mode 100644 index 00000000..b6f26de3 Binary files /dev/null and b/venv/lib/python3.12/site-packages/click/__pycache__/types.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/click/__pycache__/utils.cpython-312.pyc b/venv/lib/python3.12/site-packages/click/__pycache__/utils.cpython-312.pyc new file mode 100644 index 00000000..c77a7bd5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/click/__pycache__/utils.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/click/_compat.py b/venv/lib/python3.12/site-packages/click/_compat.py new file mode 100644 index 00000000..f2726b93 --- /dev/null +++ b/venv/lib/python3.12/site-packages/click/_compat.py @@ -0,0 +1,622 @@ +from __future__ import annotations + +import codecs +import collections.abc as cabc +import io +import os +import re +import sys +import typing as t +from types import TracebackType +from weakref import WeakKeyDictionary + +CYGWIN = sys.platform.startswith("cygwin") +WIN = sys.platform.startswith("win") +auto_wrap_for_ansi: t.Callable[[t.TextIO], t.TextIO] | None = None +_ansi_re = re.compile(r"\033\[[;?0-9]*[a-zA-Z]") + + +def _make_text_stream( + stream: t.BinaryIO, + encoding: str | None, + errors: str | None, + force_readable: bool = False, + force_writable: bool = False, +) -> t.TextIO: + if encoding is None: + encoding = get_best_encoding(stream) + if errors is None: + errors = "replace" + return _NonClosingTextIOWrapper( + stream, + encoding, + errors, + line_buffering=True, + force_readable=force_readable, + force_writable=force_writable, + ) + + +def is_ascii_encoding(encoding: str) -> bool: + """Checks if a given encoding is ascii.""" + try: + return codecs.lookup(encoding).name == "ascii" + except LookupError: + return False + + +def get_best_encoding(stream: t.IO[t.Any]) -> str: + """Returns the default stream encoding if not found.""" + rv = getattr(stream, "encoding", None) or sys.getdefaultencoding() + if is_ascii_encoding(rv): + return "utf-8" + return rv + + +class _NonClosingTextIOWrapper(io.TextIOWrapper): + def __init__( + self, + stream: t.BinaryIO, + encoding: str | None, + errors: str | None, + force_readable: bool = False, + force_writable: bool = False, + **extra: t.Any, + ) -> None: + self._stream = stream = t.cast( + t.BinaryIO, _FixupStream(stream, force_readable, force_writable) + ) + super().__init__(stream, encoding, errors, **extra) + + def __del__(self) -> None: + try: + self.detach() + except Exception: + pass + + def isatty(self) -> bool: + # https://bitbucket.org/pypy/pypy/issue/1803 + return self._stream.isatty() + + +class _FixupStream: + """The new io interface needs more from streams than streams + traditionally implement. As such, this fix-up code is necessary in + some circumstances. + + The forcing of readable and writable flags are there because some tools + put badly patched objects on sys (one such offender are certain version + of jupyter notebook). + """ + + def __init__( + self, + stream: t.BinaryIO, + force_readable: bool = False, + force_writable: bool = False, + ): + self._stream = stream + self._force_readable = force_readable + self._force_writable = force_writable + + def __getattr__(self, name: str) -> t.Any: + return getattr(self._stream, name) + + def read1(self, size: int) -> bytes: + f = getattr(self._stream, "read1", None) + + if f is not None: + return t.cast(bytes, f(size)) + + return self._stream.read(size) + + def readable(self) -> bool: + if self._force_readable: + return True + x = getattr(self._stream, "readable", None) + if x is not None: + return t.cast(bool, x()) + try: + self._stream.read(0) + except Exception: + return False + return True + + def writable(self) -> bool: + if self._force_writable: + return True + x = getattr(self._stream, "writable", None) + if x is not None: + return t.cast(bool, x()) + try: + self._stream.write(b"") + except Exception: + try: + self._stream.write(b"") + except Exception: + return False + return True + + def seekable(self) -> bool: + x = getattr(self._stream, "seekable", None) + if x is not None: + return t.cast(bool, x()) + try: + self._stream.seek(self._stream.tell()) + except Exception: + return False + return True + + +def _is_binary_reader(stream: t.IO[t.Any], default: bool = False) -> bool: + try: + return isinstance(stream.read(0), bytes) + except Exception: + return default + # This happens in some cases where the stream was already + # closed. In this case, we assume the default. + + +def _is_binary_writer(stream: t.IO[t.Any], default: bool = False) -> bool: + try: + stream.write(b"") + except Exception: + try: + stream.write("") + return False + except Exception: + pass + return default + return True + + +def _find_binary_reader(stream: t.IO[t.Any]) -> t.BinaryIO | None: + # We need to figure out if the given stream is already binary. + # This can happen because the official docs recommend detaching + # the streams to get binary streams. Some code might do this, so + # we need to deal with this case explicitly. + if _is_binary_reader(stream, False): + return t.cast(t.BinaryIO, stream) + + buf = getattr(stream, "buffer", None) + + # Same situation here; this time we assume that the buffer is + # actually binary in case it's closed. + if buf is not None and _is_binary_reader(buf, True): + return t.cast(t.BinaryIO, buf) + + return None + + +def _find_binary_writer(stream: t.IO[t.Any]) -> t.BinaryIO | None: + # We need to figure out if the given stream is already binary. + # This can happen because the official docs recommend detaching + # the streams to get binary streams. Some code might do this, so + # we need to deal with this case explicitly. + if _is_binary_writer(stream, False): + return t.cast(t.BinaryIO, stream) + + buf = getattr(stream, "buffer", None) + + # Same situation here; this time we assume that the buffer is + # actually binary in case it's closed. + if buf is not None and _is_binary_writer(buf, True): + return t.cast(t.BinaryIO, buf) + + return None + + +def _stream_is_misconfigured(stream: t.TextIO) -> bool: + """A stream is misconfigured if its encoding is ASCII.""" + # If the stream does not have an encoding set, we assume it's set + # to ASCII. This appears to happen in certain unittest + # environments. It's not quite clear what the correct behavior is + # but this at least will force Click to recover somehow. + return is_ascii_encoding(getattr(stream, "encoding", None) or "ascii") + + +def _is_compat_stream_attr(stream: t.TextIO, attr: str, value: str | None) -> bool: + """A stream attribute is compatible if it is equal to the + desired value or the desired value is unset and the attribute + has a value. + """ + stream_value = getattr(stream, attr, None) + return stream_value == value or (value is None and stream_value is not None) + + +def _is_compatible_text_stream( + stream: t.TextIO, encoding: str | None, errors: str | None +) -> bool: + """Check if a stream's encoding and errors attributes are + compatible with the desired values. + """ + return _is_compat_stream_attr( + stream, "encoding", encoding + ) and _is_compat_stream_attr(stream, "errors", errors) + + +def _force_correct_text_stream( + text_stream: t.IO[t.Any], + encoding: str | None, + errors: str | None, + is_binary: t.Callable[[t.IO[t.Any], bool], bool], + find_binary: t.Callable[[t.IO[t.Any]], t.BinaryIO | None], + force_readable: bool = False, + force_writable: bool = False, +) -> t.TextIO: + if is_binary(text_stream, False): + binary_reader = t.cast(t.BinaryIO, text_stream) + else: + text_stream = t.cast(t.TextIO, text_stream) + # If the stream looks compatible, and won't default to a + # misconfigured ascii encoding, return it as-is. + if _is_compatible_text_stream(text_stream, encoding, errors) and not ( + encoding is None and _stream_is_misconfigured(text_stream) + ): + return text_stream + + # Otherwise, get the underlying binary reader. + possible_binary_reader = find_binary(text_stream) + + # If that's not possible, silently use the original reader + # and get mojibake instead of exceptions. + if possible_binary_reader is None: + return text_stream + + binary_reader = possible_binary_reader + + # Default errors to replace instead of strict in order to get + # something that works. + if errors is None: + errors = "replace" + + # Wrap the binary stream in a text stream with the correct + # encoding parameters. + return _make_text_stream( + binary_reader, + encoding, + errors, + force_readable=force_readable, + force_writable=force_writable, + ) + + +def _force_correct_text_reader( + text_reader: t.IO[t.Any], + encoding: str | None, + errors: str | None, + force_readable: bool = False, +) -> t.TextIO: + return _force_correct_text_stream( + text_reader, + encoding, + errors, + _is_binary_reader, + _find_binary_reader, + force_readable=force_readable, + ) + + +def _force_correct_text_writer( + text_writer: t.IO[t.Any], + encoding: str | None, + errors: str | None, + force_writable: bool = False, +) -> t.TextIO: + return _force_correct_text_stream( + text_writer, + encoding, + errors, + _is_binary_writer, + _find_binary_writer, + force_writable=force_writable, + ) + + +def get_binary_stdin() -> t.BinaryIO: + reader = _find_binary_reader(sys.stdin) + if reader is None: + raise RuntimeError("Was not able to determine binary stream for sys.stdin.") + return reader + + +def get_binary_stdout() -> t.BinaryIO: + writer = _find_binary_writer(sys.stdout) + if writer is None: + raise RuntimeError("Was not able to determine binary stream for sys.stdout.") + return writer + + +def get_binary_stderr() -> t.BinaryIO: + writer = _find_binary_writer(sys.stderr) + if writer is None: + raise RuntimeError("Was not able to determine binary stream for sys.stderr.") + return writer + + +def get_text_stdin(encoding: str | None = None, errors: str | None = None) -> t.TextIO: + rv = _get_windows_console_stream(sys.stdin, encoding, errors) + if rv is not None: + return rv + return _force_correct_text_reader(sys.stdin, encoding, errors, force_readable=True) + + +def get_text_stdout(encoding: str | None = None, errors: str | None = None) -> t.TextIO: + rv = _get_windows_console_stream(sys.stdout, encoding, errors) + if rv is not None: + return rv + return _force_correct_text_writer(sys.stdout, encoding, errors, force_writable=True) + + +def get_text_stderr(encoding: str | None = None, errors: str | None = None) -> t.TextIO: + rv = _get_windows_console_stream(sys.stderr, encoding, errors) + if rv is not None: + return rv + return _force_correct_text_writer(sys.stderr, encoding, errors, force_writable=True) + + +def _wrap_io_open( + file: str | os.PathLike[str] | int, + mode: str, + encoding: str | None, + errors: str | None, +) -> t.IO[t.Any]: + """Handles not passing ``encoding`` and ``errors`` in binary mode.""" + if "b" in mode: + return open(file, mode) + + return open(file, mode, encoding=encoding, errors=errors) + + +def open_stream( + filename: str | os.PathLike[str], + mode: str = "r", + encoding: str | None = None, + errors: str | None = "strict", + atomic: bool = False, +) -> tuple[t.IO[t.Any], bool]: + binary = "b" in mode + filename = os.fspath(filename) + + # Standard streams first. These are simple because they ignore the + # atomic flag. Use fsdecode to handle Path("-"). + if os.fsdecode(filename) == "-": + if any(m in mode for m in ["w", "a", "x"]): + if binary: + return get_binary_stdout(), False + return get_text_stdout(encoding=encoding, errors=errors), False + if binary: + return get_binary_stdin(), False + return get_text_stdin(encoding=encoding, errors=errors), False + + # Non-atomic writes directly go out through the regular open functions. + if not atomic: + return _wrap_io_open(filename, mode, encoding, errors), True + + # Some usability stuff for atomic writes + if "a" in mode: + raise ValueError( + "Appending to an existing file is not supported, because that" + " would involve an expensive `copy`-operation to a temporary" + " file. Open the file in normal `w`-mode and copy explicitly" + " if that's what you're after." + ) + if "x" in mode: + raise ValueError("Use the `overwrite`-parameter instead.") + if "w" not in mode: + raise ValueError("Atomic writes only make sense with `w`-mode.") + + # Atomic writes are more complicated. They work by opening a file + # as a proxy in the same folder and then using the fdopen + # functionality to wrap it in a Python file. Then we wrap it in an + # atomic file that moves the file over on close. + import errno + import random + + try: + perm: int | None = os.stat(filename).st_mode + except OSError: + perm = None + + flags = os.O_RDWR | os.O_CREAT | os.O_EXCL + + if binary: + flags |= getattr(os, "O_BINARY", 0) + + while True: + tmp_filename = os.path.join( + os.path.dirname(filename), + f".__atomic-write{random.randrange(1 << 32):08x}", + ) + try: + fd = os.open(tmp_filename, flags, 0o666 if perm is None else perm) + break + except OSError as e: + if e.errno == errno.EEXIST or ( + os.name == "nt" + and e.errno == errno.EACCES + and os.path.isdir(e.filename) + and os.access(e.filename, os.W_OK) + ): + continue + raise + + if perm is not None: + os.chmod(tmp_filename, perm) # in case perm includes bits in umask + + f = _wrap_io_open(fd, mode, encoding, errors) + af = _AtomicFile(f, tmp_filename, os.path.realpath(filename)) + return t.cast(t.IO[t.Any], af), True + + +class _AtomicFile: + def __init__(self, f: t.IO[t.Any], tmp_filename: str, real_filename: str) -> None: + self._f = f + self._tmp_filename = tmp_filename + self._real_filename = real_filename + self.closed = False + + @property + def name(self) -> str: + return self._real_filename + + def close(self, delete: bool = False) -> None: + if self.closed: + return + self._f.close() + os.replace(self._tmp_filename, self._real_filename) + self.closed = True + + def __getattr__(self, name: str) -> t.Any: + return getattr(self._f, name) + + def __enter__(self) -> _AtomicFile: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + tb: TracebackType | None, + ) -> None: + self.close(delete=exc_type is not None) + + def __repr__(self) -> str: + return repr(self._f) + + +def strip_ansi(value: str) -> str: + return _ansi_re.sub("", value) + + +def _is_jupyter_kernel_output(stream: t.IO[t.Any]) -> bool: + while isinstance(stream, (_FixupStream, _NonClosingTextIOWrapper)): + stream = stream._stream + + return stream.__class__.__module__.startswith("ipykernel.") + + +def should_strip_ansi( + stream: t.IO[t.Any] | None = None, color: bool | None = None +) -> bool: + if color is None: + if stream is None: + stream = sys.stdin + return not isatty(stream) and not _is_jupyter_kernel_output(stream) + return not color + + +# On Windows, wrap the output streams with colorama to support ANSI +# color codes. +# NOTE: double check is needed so mypy does not analyze this on Linux +if sys.platform.startswith("win") and WIN: + from ._winconsole import _get_windows_console_stream + + def _get_argv_encoding() -> str: + import locale + + return locale.getpreferredencoding() + + _ansi_stream_wrappers: cabc.MutableMapping[t.TextIO, t.TextIO] = WeakKeyDictionary() + + def auto_wrap_for_ansi(stream: t.TextIO, color: bool | None = None) -> t.TextIO: + """Support ANSI color and style codes on Windows by wrapping a + stream with colorama. + """ + try: + cached = _ansi_stream_wrappers.get(stream) + except Exception: + cached = None + + if cached is not None: + return cached + + import colorama + + strip = should_strip_ansi(stream, color) + ansi_wrapper = colorama.AnsiToWin32(stream, strip=strip) + rv = t.cast(t.TextIO, ansi_wrapper.stream) + _write = rv.write + + def _safe_write(s: str) -> int: + try: + return _write(s) + except BaseException: + ansi_wrapper.reset_all() + raise + + rv.write = _safe_write # type: ignore[method-assign] + + try: + _ansi_stream_wrappers[stream] = rv + except Exception: + pass + + return rv + +else: + + def _get_argv_encoding() -> str: + return getattr(sys.stdin, "encoding", None) or sys.getfilesystemencoding() + + def _get_windows_console_stream( + f: t.TextIO, encoding: str | None, errors: str | None + ) -> t.TextIO | None: + return None + + +def term_len(x: str) -> int: + return len(strip_ansi(x)) + + +def isatty(stream: t.IO[t.Any]) -> bool: + try: + return stream.isatty() + except Exception: + return False + + +def _make_cached_stream_func( + src_func: t.Callable[[], t.TextIO | None], + wrapper_func: t.Callable[[], t.TextIO], +) -> t.Callable[[], t.TextIO | None]: + cache: cabc.MutableMapping[t.TextIO, t.TextIO] = WeakKeyDictionary() + + def func() -> t.TextIO | None: + stream = src_func() + + if stream is None: + return None + + try: + rv = cache.get(stream) + except Exception: + rv = None + if rv is not None: + return rv + rv = wrapper_func() + try: + cache[stream] = rv + except Exception: + pass + return rv + + return func + + +_default_text_stdin = _make_cached_stream_func(lambda: sys.stdin, get_text_stdin) +_default_text_stdout = _make_cached_stream_func(lambda: sys.stdout, get_text_stdout) +_default_text_stderr = _make_cached_stream_func(lambda: sys.stderr, get_text_stderr) + + +binary_streams: cabc.Mapping[str, t.Callable[[], t.BinaryIO]] = { + "stdin": get_binary_stdin, + "stdout": get_binary_stdout, + "stderr": get_binary_stderr, +} + +text_streams: cabc.Mapping[str, t.Callable[[str | None, str | None], t.TextIO]] = { + "stdin": get_text_stdin, + "stdout": get_text_stdout, + "stderr": get_text_stderr, +} diff --git a/venv/lib/python3.12/site-packages/click/_termui_impl.py b/venv/lib/python3.12/site-packages/click/_termui_impl.py new file mode 100644 index 00000000..51fd9bf3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/click/_termui_impl.py @@ -0,0 +1,839 @@ +""" +This module contains implementations for the termui module. To keep the +import time of Click down, some infrequently used functionality is +placed in this module and only imported as needed. +""" + +from __future__ import annotations + +import collections.abc as cabc +import contextlib +import math +import os +import shlex +import sys +import time +import typing as t +from gettext import gettext as _ +from io import StringIO +from pathlib import Path +from shutil import which +from types import TracebackType + +from ._compat import _default_text_stdout +from ._compat import CYGWIN +from ._compat import get_best_encoding +from ._compat import isatty +from ._compat import open_stream +from ._compat import strip_ansi +from ._compat import term_len +from ._compat import WIN +from .exceptions import ClickException +from .utils import echo + +V = t.TypeVar("V") + +if os.name == "nt": + BEFORE_BAR = "\r" + AFTER_BAR = "\n" +else: + BEFORE_BAR = "\r\033[?25l" + AFTER_BAR = "\033[?25h\n" + + +class ProgressBar(t.Generic[V]): + def __init__( + self, + iterable: cabc.Iterable[V] | None, + length: int | None = None, + fill_char: str = "#", + empty_char: str = " ", + bar_template: str = "%(bar)s", + info_sep: str = " ", + hidden: bool = False, + show_eta: bool = True, + show_percent: bool | None = None, + show_pos: bool = False, + item_show_func: t.Callable[[V | None], str | None] | None = None, + label: str | None = None, + file: t.TextIO | None = None, + color: bool | None = None, + update_min_steps: int = 1, + width: int = 30, + ) -> None: + self.fill_char = fill_char + self.empty_char = empty_char + self.bar_template = bar_template + self.info_sep = info_sep + self.hidden = hidden + self.show_eta = show_eta + self.show_percent = show_percent + self.show_pos = show_pos + self.item_show_func = item_show_func + self.label: str = label or "" + + if file is None: + file = _default_text_stdout() + + # There are no standard streams attached to write to. For example, + # pythonw on Windows. + if file is None: + file = StringIO() + + self.file = file + self.color = color + self.update_min_steps = update_min_steps + self._completed_intervals = 0 + self.width: int = width + self.autowidth: bool = width == 0 + + if length is None: + from operator import length_hint + + length = length_hint(iterable, -1) + + if length == -1: + length = None + if iterable is None: + if length is None: + raise TypeError("iterable or length is required") + iterable = t.cast("cabc.Iterable[V]", range(length)) + self.iter: cabc.Iterable[V] = iter(iterable) + self.length = length + self.pos: int = 0 + self.avg: list[float] = [] + self.last_eta: float + self.start: float + self.start = self.last_eta = time.time() + self.eta_known: bool = False + self.finished: bool = False + self.max_width: int | None = None + self.entered: bool = False + self.current_item: V | None = None + self._is_atty = isatty(self.file) + self._last_line: str | None = None + + def __enter__(self) -> ProgressBar[V]: + self.entered = True + self.render_progress() + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + tb: TracebackType | None, + ) -> None: + self.render_finish() + + def __iter__(self) -> cabc.Iterator[V]: + if not self.entered: + raise RuntimeError("You need to use progress bars in a with block.") + self.render_progress() + return self.generator() + + def __next__(self) -> V: + # Iteration is defined in terms of a generator function, + # returned by iter(self); use that to define next(). This works + # because `self.iter` is an iterable consumed by that generator, + # so it is re-entry safe. Calling `next(self.generator())` + # twice works and does "what you want". + return next(iter(self)) + + def render_finish(self) -> None: + if self.hidden or not self._is_atty: + return + self.file.write(AFTER_BAR) + self.file.flush() + + @property + def pct(self) -> float: + if self.finished: + return 1.0 + return min(self.pos / (float(self.length or 1) or 1), 1.0) + + @property + def time_per_iteration(self) -> float: + if not self.avg: + return 0.0 + return sum(self.avg) / float(len(self.avg)) + + @property + def eta(self) -> float: + if self.length is not None and not self.finished: + return self.time_per_iteration * (self.length - self.pos) + return 0.0 + + def format_eta(self) -> str: + if self.eta_known: + t = int(self.eta) + seconds = t % 60 + t //= 60 + minutes = t % 60 + t //= 60 + hours = t % 24 + t //= 24 + if t > 0: + return f"{t}d {hours:02}:{minutes:02}:{seconds:02}" + else: + return f"{hours:02}:{minutes:02}:{seconds:02}" + return "" + + def format_pos(self) -> str: + pos = str(self.pos) + if self.length is not None: + pos += f"/{self.length}" + return pos + + def format_pct(self) -> str: + return f"{int(self.pct * 100): 4}%"[1:] + + def format_bar(self) -> str: + if self.length is not None: + bar_length = int(self.pct * self.width) + bar = self.fill_char * bar_length + bar += self.empty_char * (self.width - bar_length) + elif self.finished: + bar = self.fill_char * self.width + else: + chars = list(self.empty_char * (self.width or 1)) + if self.time_per_iteration != 0: + chars[ + int( + (math.cos(self.pos * self.time_per_iteration) / 2.0 + 0.5) + * self.width + ) + ] = self.fill_char + bar = "".join(chars) + return bar + + def format_progress_line(self) -> str: + show_percent = self.show_percent + + info_bits = [] + if self.length is not None and show_percent is None: + show_percent = not self.show_pos + + if self.show_pos: + info_bits.append(self.format_pos()) + if show_percent: + info_bits.append(self.format_pct()) + if self.show_eta and self.eta_known and not self.finished: + info_bits.append(self.format_eta()) + if self.item_show_func is not None: + item_info = self.item_show_func(self.current_item) + if item_info is not None: + info_bits.append(item_info) + + return ( + self.bar_template + % { + "label": self.label, + "bar": self.format_bar(), + "info": self.info_sep.join(info_bits), + } + ).rstrip() + + def render_progress(self) -> None: + import shutil + + if self.hidden: + return + + if not self._is_atty: + # Only output the label once if the output is not a TTY. + if self._last_line != self.label: + self._last_line = self.label + echo(self.label, file=self.file, color=self.color) + return + + buf = [] + # Update width in case the terminal has been resized + if self.autowidth: + old_width = self.width + self.width = 0 + clutter_length = term_len(self.format_progress_line()) + new_width = max(0, shutil.get_terminal_size().columns - clutter_length) + if new_width < old_width and self.max_width is not None: + buf.append(BEFORE_BAR) + buf.append(" " * self.max_width) + self.max_width = new_width + self.width = new_width + + clear_width = self.width + if self.max_width is not None: + clear_width = self.max_width + + buf.append(BEFORE_BAR) + line = self.format_progress_line() + line_len = term_len(line) + if self.max_width is None or self.max_width < line_len: + self.max_width = line_len + + buf.append(line) + buf.append(" " * (clear_width - line_len)) + line = "".join(buf) + # Render the line only if it changed. + + if line != self._last_line: + self._last_line = line + echo(line, file=self.file, color=self.color, nl=False) + self.file.flush() + + def make_step(self, n_steps: int) -> None: + self.pos += n_steps + if self.length is not None and self.pos >= self.length: + self.finished = True + + if (time.time() - self.last_eta) < 1.0: + return + + self.last_eta = time.time() + + # self.avg is a rolling list of length <= 7 of steps where steps are + # defined as time elapsed divided by the total progress through + # self.length. + if self.pos: + step = (time.time() - self.start) / self.pos + else: + step = time.time() - self.start + + self.avg = self.avg[-6:] + [step] + + self.eta_known = self.length is not None + + def update(self, n_steps: int, current_item: V | None = None) -> None: + """Update the progress bar by advancing a specified number of + steps, and optionally set the ``current_item`` for this new + position. + + :param n_steps: Number of steps to advance. + :param current_item: Optional item to set as ``current_item`` + for the updated position. + + .. versionchanged:: 8.0 + Added the ``current_item`` optional parameter. + + .. versionchanged:: 8.0 + Only render when the number of steps meets the + ``update_min_steps`` threshold. + """ + if current_item is not None: + self.current_item = current_item + + self._completed_intervals += n_steps + + if self._completed_intervals >= self.update_min_steps: + self.make_step(self._completed_intervals) + self.render_progress() + self._completed_intervals = 0 + + def finish(self) -> None: + self.eta_known = False + self.current_item = None + self.finished = True + + def generator(self) -> cabc.Iterator[V]: + """Return a generator which yields the items added to the bar + during construction, and updates the progress bar *after* the + yielded block returns. + """ + # WARNING: the iterator interface for `ProgressBar` relies on + # this and only works because this is a simple generator which + # doesn't create or manage additional state. If this function + # changes, the impact should be evaluated both against + # `iter(bar)` and `next(bar)`. `next()` in particular may call + # `self.generator()` repeatedly, and this must remain safe in + # order for that interface to work. + if not self.entered: + raise RuntimeError("You need to use progress bars in a with block.") + + if not self._is_atty: + yield from self.iter + else: + for rv in self.iter: + self.current_item = rv + + # This allows show_item_func to be updated before the + # item is processed. Only trigger at the beginning of + # the update interval. + if self._completed_intervals == 0: + self.render_progress() + + yield rv + self.update(1) + + self.finish() + self.render_progress() + + +def pager(generator: cabc.Iterable[str], color: bool | None = None) -> None: + """Decide what method to use for paging through text.""" + stdout = _default_text_stdout() + + # There are no standard streams attached to write to. For example, + # pythonw on Windows. + if stdout is None: + stdout = StringIO() + + if not isatty(sys.stdin) or not isatty(stdout): + return _nullpager(stdout, generator, color) + + # Split and normalize the pager command into parts. + pager_cmd_parts = shlex.split(os.environ.get("PAGER", ""), posix=False) + if pager_cmd_parts: + if WIN: + if _tempfilepager(generator, pager_cmd_parts, color): + return + elif _pipepager(generator, pager_cmd_parts, color): + return + + if os.environ.get("TERM") in ("dumb", "emacs"): + return _nullpager(stdout, generator, color) + if (WIN or sys.platform.startswith("os2")) and _tempfilepager( + generator, ["more"], color + ): + return + if _pipepager(generator, ["less"], color): + return + + import tempfile + + fd, filename = tempfile.mkstemp() + os.close(fd) + try: + if _pipepager(generator, ["more"], color): + return + return _nullpager(stdout, generator, color) + finally: + os.unlink(filename) + + +def _pipepager( + generator: cabc.Iterable[str], cmd_parts: list[str], color: bool | None +) -> bool: + """Page through text by feeding it to another program. Invoking a + pager through this might support colors. + + Returns `True` if the command was found, `False` otherwise and thus another + pager should be attempted. + """ + # Split the command into the invoked CLI and its parameters. + if not cmd_parts: + return False + cmd = cmd_parts[0] + cmd_params = cmd_parts[1:] + + cmd_filepath = which(cmd) + if not cmd_filepath: + return False + # Resolves symlinks and produces a normalized absolute path string. + cmd_path = Path(cmd_filepath).resolve() + cmd_name = cmd_path.name + + import subprocess + + # Make a local copy of the environment to not affect the global one. + env = dict(os.environ) + + # If we're piping to less and the user hasn't decided on colors, we enable + # them by default we find the -R flag in the command line arguments. + if color is None and cmd_name == "less": + less_flags = f"{os.environ.get('LESS', '')}{' '.join(cmd_params)}" + if not less_flags: + env["LESS"] = "-R" + color = True + elif "r" in less_flags or "R" in less_flags: + color = True + + c = subprocess.Popen( + [str(cmd_path)] + cmd_params, + shell=True, + stdin=subprocess.PIPE, + env=env, + errors="replace", + text=True, + ) + assert c.stdin is not None + try: + for text in generator: + if not color: + text = strip_ansi(text) + + c.stdin.write(text) + except BrokenPipeError: + # In case the pager exited unexpectedly, ignore the broken pipe error. + pass + except Exception as e: + # In case there is an exception we want to close the pager immediately + # and let the caller handle it. + # Otherwise the pager will keep running, and the user may not notice + # the error message, or worse yet it may leave the terminal in a broken state. + c.terminate() + raise e + finally: + # We must close stdin and wait for the pager to exit before we continue + try: + c.stdin.close() + # Close implies flush, so it might throw a BrokenPipeError if the pager + # process exited already. + except BrokenPipeError: + pass + + # Less doesn't respect ^C, but catches it for its own UI purposes (aborting + # search or other commands inside less). + # + # That means when the user hits ^C, the parent process (click) terminates, + # but less is still alive, paging the output and messing up the terminal. + # + # If the user wants to make the pager exit on ^C, they should set + # `LESS='-K'`. It's not our decision to make. + while True: + try: + c.wait() + except KeyboardInterrupt: + pass + else: + break + + return True + + +def _tempfilepager( + generator: cabc.Iterable[str], cmd_parts: list[str], color: bool | None +) -> bool: + """Page through text by invoking a program on a temporary file. + + Returns `True` if the command was found, `False` otherwise and thus another + pager should be attempted. + """ + # Split the command into the invoked CLI and its parameters. + if not cmd_parts: + return False + cmd = cmd_parts[0] + + cmd_filepath = which(cmd) + if not cmd_filepath: + return False + # Resolves symlinks and produces a normalized absolute path string. + cmd_path = Path(cmd_filepath).resolve() + + import subprocess + import tempfile + + fd, filename = tempfile.mkstemp() + # TODO: This never terminates if the passed generator never terminates. + text = "".join(generator) + if not color: + text = strip_ansi(text) + encoding = get_best_encoding(sys.stdout) + with open_stream(filename, "wb")[0] as f: + f.write(text.encode(encoding)) + try: + subprocess.call([str(cmd_path), filename]) + except OSError: + # Command not found + pass + finally: + os.close(fd) + os.unlink(filename) + + return True + + +def _nullpager( + stream: t.TextIO, generator: cabc.Iterable[str], color: bool | None +) -> None: + """Simply print unformatted text. This is the ultimate fallback.""" + for text in generator: + if not color: + text = strip_ansi(text) + stream.write(text) + + +class Editor: + def __init__( + self, + editor: str | None = None, + env: cabc.Mapping[str, str] | None = None, + require_save: bool = True, + extension: str = ".txt", + ) -> None: + self.editor = editor + self.env = env + self.require_save = require_save + self.extension = extension + + def get_editor(self) -> str: + if self.editor is not None: + return self.editor + for key in "VISUAL", "EDITOR": + rv = os.environ.get(key) + if rv: + return rv + if WIN: + return "notepad" + for editor in "sensible-editor", "vim", "nano": + if which(editor) is not None: + return editor + return "vi" + + def edit_files(self, filenames: cabc.Iterable[str]) -> None: + import subprocess + + editor = self.get_editor() + environ: dict[str, str] | None = None + + if self.env: + environ = os.environ.copy() + environ.update(self.env) + + exc_filename = " ".join(f'"{filename}"' for filename in filenames) + + try: + c = subprocess.Popen( + args=f"{editor} {exc_filename}", env=environ, shell=True + ) + exit_code = c.wait() + if exit_code != 0: + raise ClickException( + _("{editor}: Editing failed").format(editor=editor) + ) + except OSError as e: + raise ClickException( + _("{editor}: Editing failed: {e}").format(editor=editor, e=e) + ) from e + + @t.overload + def edit(self, text: bytes | bytearray) -> bytes | None: ... + + # We cannot know whether or not the type expected is str or bytes when None + # is passed, so str is returned as that was what was done before. + @t.overload + def edit(self, text: str | None) -> str | None: ... + + def edit(self, text: str | bytes | bytearray | None) -> str | bytes | None: + import tempfile + + if text is None: + data = b"" + elif isinstance(text, (bytes, bytearray)): + data = text + else: + if text and not text.endswith("\n"): + text += "\n" + + if WIN: + data = text.replace("\n", "\r\n").encode("utf-8-sig") + else: + data = text.encode("utf-8") + + fd, name = tempfile.mkstemp(prefix="editor-", suffix=self.extension) + f: t.BinaryIO + + try: + with os.fdopen(fd, "wb") as f: + f.write(data) + + # If the filesystem resolution is 1 second, like Mac OS + # 10.12 Extended, or 2 seconds, like FAT32, and the editor + # closes very fast, require_save can fail. Set the modified + # time to be 2 seconds in the past to work around this. + os.utime(name, (os.path.getatime(name), os.path.getmtime(name) - 2)) + # Depending on the resolution, the exact value might not be + # recorded, so get the new recorded value. + timestamp = os.path.getmtime(name) + + self.edit_files((name,)) + + if self.require_save and os.path.getmtime(name) == timestamp: + return None + + with open(name, "rb") as f: + rv = f.read() + + if isinstance(text, (bytes, bytearray)): + return rv + + return rv.decode("utf-8-sig").replace("\r\n", "\n") + finally: + os.unlink(name) + + +def open_url(url: str, wait: bool = False, locate: bool = False) -> int: + import subprocess + + def _unquote_file(url: str) -> str: + from urllib.parse import unquote + + if url.startswith("file://"): + url = unquote(url[7:]) + + return url + + if sys.platform == "darwin": + args = ["open"] + if wait: + args.append("-W") + if locate: + args.append("-R") + args.append(_unquote_file(url)) + null = open("/dev/null", "w") + try: + return subprocess.Popen(args, stderr=null).wait() + finally: + null.close() + elif WIN: + if locate: + url = _unquote_file(url) + args = ["explorer", f"/select,{url}"] + else: + args = ["start"] + if wait: + args.append("/WAIT") + args.append("") + args.append(url) + try: + return subprocess.call(args) + except OSError: + # Command not found + return 127 + elif CYGWIN: + if locate: + url = _unquote_file(url) + args = ["cygstart", os.path.dirname(url)] + else: + args = ["cygstart"] + if wait: + args.append("-w") + args.append(url) + try: + return subprocess.call(args) + except OSError: + # Command not found + return 127 + + try: + if locate: + url = os.path.dirname(_unquote_file(url)) or "." + else: + url = _unquote_file(url) + c = subprocess.Popen(["xdg-open", url]) + if wait: + return c.wait() + return 0 + except OSError: + if url.startswith(("http://", "https://")) and not locate and not wait: + import webbrowser + + webbrowser.open(url) + return 0 + return 1 + + +def _translate_ch_to_exc(ch: str) -> None: + if ch == "\x03": + raise KeyboardInterrupt() + + if ch == "\x04" and not WIN: # Unix-like, Ctrl+D + raise EOFError() + + if ch == "\x1a" and WIN: # Windows, Ctrl+Z + raise EOFError() + + return None + + +if sys.platform == "win32": + import msvcrt + + @contextlib.contextmanager + def raw_terminal() -> cabc.Iterator[int]: + yield -1 + + def getchar(echo: bool) -> str: + # The function `getch` will return a bytes object corresponding to + # the pressed character. Since Windows 10 build 1803, it will also + # return \x00 when called a second time after pressing a regular key. + # + # `getwch` does not share this probably-bugged behavior. Moreover, it + # returns a Unicode object by default, which is what we want. + # + # Either of these functions will return \x00 or \xe0 to indicate + # a special key, and you need to call the same function again to get + # the "rest" of the code. The fun part is that \u00e0 is + # "latin small letter a with grave", so if you type that on a French + # keyboard, you _also_ get a \xe0. + # E.g., consider the Up arrow. This returns \xe0 and then \x48. The + # resulting Unicode string reads as "a with grave" + "capital H". + # This is indistinguishable from when the user actually types + # "a with grave" and then "capital H". + # + # When \xe0 is returned, we assume it's part of a special-key sequence + # and call `getwch` again, but that means that when the user types + # the \u00e0 character, `getchar` doesn't return until a second + # character is typed. + # The alternative is returning immediately, but that would mess up + # cross-platform handling of arrow keys and others that start with + # \xe0. Another option is using `getch`, but then we can't reliably + # read non-ASCII characters, because return values of `getch` are + # limited to the current 8-bit codepage. + # + # Anyway, Click doesn't claim to do this Right(tm), and using `getwch` + # is doing the right thing in more situations than with `getch`. + + if echo: + func = t.cast(t.Callable[[], str], msvcrt.getwche) + else: + func = t.cast(t.Callable[[], str], msvcrt.getwch) + + rv = func() + + if rv in ("\x00", "\xe0"): + # \x00 and \xe0 are control characters that indicate special key, + # see above. + rv += func() + + _translate_ch_to_exc(rv) + return rv + +else: + import termios + import tty + + @contextlib.contextmanager + def raw_terminal() -> cabc.Iterator[int]: + f: t.TextIO | None + fd: int + + if not isatty(sys.stdin): + f = open("/dev/tty") + fd = f.fileno() + else: + fd = sys.stdin.fileno() + f = None + + try: + old_settings = termios.tcgetattr(fd) + + try: + tty.setraw(fd) + yield fd + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + sys.stdout.flush() + + if f is not None: + f.close() + except termios.error: + pass + + def getchar(echo: bool) -> str: + with raw_terminal() as fd: + ch = os.read(fd, 32).decode(get_best_encoding(sys.stdin), "replace") + + if echo and isatty(sys.stdout): + sys.stdout.write(ch) + + _translate_ch_to_exc(ch) + return ch diff --git a/venv/lib/python3.12/site-packages/click/_textwrap.py b/venv/lib/python3.12/site-packages/click/_textwrap.py new file mode 100644 index 00000000..97fbee3d --- /dev/null +++ b/venv/lib/python3.12/site-packages/click/_textwrap.py @@ -0,0 +1,51 @@ +from __future__ import annotations + +import collections.abc as cabc +import textwrap +from contextlib import contextmanager + + +class TextWrapper(textwrap.TextWrapper): + def _handle_long_word( + self, + reversed_chunks: list[str], + cur_line: list[str], + cur_len: int, + width: int, + ) -> None: + space_left = max(width - cur_len, 1) + + if self.break_long_words: + last = reversed_chunks[-1] + cut = last[:space_left] + res = last[space_left:] + cur_line.append(cut) + reversed_chunks[-1] = res + elif not cur_line: + cur_line.append(reversed_chunks.pop()) + + @contextmanager + def extra_indent(self, indent: str) -> cabc.Iterator[None]: + old_initial_indent = self.initial_indent + old_subsequent_indent = self.subsequent_indent + self.initial_indent += indent + self.subsequent_indent += indent + + try: + yield + finally: + self.initial_indent = old_initial_indent + self.subsequent_indent = old_subsequent_indent + + def indent_only(self, text: str) -> str: + rv = [] + + for idx, line in enumerate(text.splitlines()): + indent = self.initial_indent + + if idx > 0: + indent = self.subsequent_indent + + rv.append(f"{indent}{line}") + + return "\n".join(rv) diff --git a/venv/lib/python3.12/site-packages/click/_winconsole.py b/venv/lib/python3.12/site-packages/click/_winconsole.py new file mode 100644 index 00000000..e56c7c6a --- /dev/null +++ b/venv/lib/python3.12/site-packages/click/_winconsole.py @@ -0,0 +1,296 @@ +# This module is based on the excellent work by Adam Bartoš who +# provided a lot of what went into the implementation here in +# the discussion to issue1602 in the Python bug tracker. +# +# There are some general differences in regards to how this works +# compared to the original patches as we do not need to patch +# the entire interpreter but just work in our little world of +# echo and prompt. +from __future__ import annotations + +import collections.abc as cabc +import io +import sys +import time +import typing as t +from ctypes import Array +from ctypes import byref +from ctypes import c_char +from ctypes import c_char_p +from ctypes import c_int +from ctypes import c_ssize_t +from ctypes import c_ulong +from ctypes import c_void_p +from ctypes import POINTER +from ctypes import py_object +from ctypes import Structure +from ctypes.wintypes import DWORD +from ctypes.wintypes import HANDLE +from ctypes.wintypes import LPCWSTR +from ctypes.wintypes import LPWSTR + +from ._compat import _NonClosingTextIOWrapper + +assert sys.platform == "win32" +import msvcrt # noqa: E402 +from ctypes import windll # noqa: E402 +from ctypes import WINFUNCTYPE # noqa: E402 + +c_ssize_p = POINTER(c_ssize_t) + +kernel32 = windll.kernel32 +GetStdHandle = kernel32.GetStdHandle +ReadConsoleW = kernel32.ReadConsoleW +WriteConsoleW = kernel32.WriteConsoleW +GetConsoleMode = kernel32.GetConsoleMode +GetLastError = kernel32.GetLastError +GetCommandLineW = WINFUNCTYPE(LPWSTR)(("GetCommandLineW", windll.kernel32)) +CommandLineToArgvW = WINFUNCTYPE(POINTER(LPWSTR), LPCWSTR, POINTER(c_int))( + ("CommandLineToArgvW", windll.shell32) +) +LocalFree = WINFUNCTYPE(c_void_p, c_void_p)(("LocalFree", windll.kernel32)) + +STDIN_HANDLE = GetStdHandle(-10) +STDOUT_HANDLE = GetStdHandle(-11) +STDERR_HANDLE = GetStdHandle(-12) + +PyBUF_SIMPLE = 0 +PyBUF_WRITABLE = 1 + +ERROR_SUCCESS = 0 +ERROR_NOT_ENOUGH_MEMORY = 8 +ERROR_OPERATION_ABORTED = 995 + +STDIN_FILENO = 0 +STDOUT_FILENO = 1 +STDERR_FILENO = 2 + +EOF = b"\x1a" +MAX_BYTES_WRITTEN = 32767 + +if t.TYPE_CHECKING: + try: + # Using `typing_extensions.Buffer` instead of `collections.abc` + # on Windows for some reason does not have `Sized` implemented. + from collections.abc import Buffer # type: ignore + except ImportError: + from typing_extensions import Buffer + +try: + from ctypes import pythonapi +except ImportError: + # On PyPy we cannot get buffers so our ability to operate here is + # severely limited. + get_buffer = None +else: + + class Py_buffer(Structure): + _fields_ = [ # noqa: RUF012 + ("buf", c_void_p), + ("obj", py_object), + ("len", c_ssize_t), + ("itemsize", c_ssize_t), + ("readonly", c_int), + ("ndim", c_int), + ("format", c_char_p), + ("shape", c_ssize_p), + ("strides", c_ssize_p), + ("suboffsets", c_ssize_p), + ("internal", c_void_p), + ] + + PyObject_GetBuffer = pythonapi.PyObject_GetBuffer + PyBuffer_Release = pythonapi.PyBuffer_Release + + def get_buffer(obj: Buffer, writable: bool = False) -> Array[c_char]: + buf = Py_buffer() + flags: int = PyBUF_WRITABLE if writable else PyBUF_SIMPLE + PyObject_GetBuffer(py_object(obj), byref(buf), flags) + + try: + buffer_type = c_char * buf.len + out: Array[c_char] = buffer_type.from_address(buf.buf) + return out + finally: + PyBuffer_Release(byref(buf)) + + +class _WindowsConsoleRawIOBase(io.RawIOBase): + def __init__(self, handle: int | None) -> None: + self.handle = handle + + def isatty(self) -> t.Literal[True]: + super().isatty() + return True + + +class _WindowsConsoleReader(_WindowsConsoleRawIOBase): + def readable(self) -> t.Literal[True]: + return True + + def readinto(self, b: Buffer) -> int: + bytes_to_be_read = len(b) + if not bytes_to_be_read: + return 0 + elif bytes_to_be_read % 2: + raise ValueError( + "cannot read odd number of bytes from UTF-16-LE encoded console" + ) + + buffer = get_buffer(b, writable=True) + code_units_to_be_read = bytes_to_be_read // 2 + code_units_read = c_ulong() + + rv = ReadConsoleW( + HANDLE(self.handle), + buffer, + code_units_to_be_read, + byref(code_units_read), + None, + ) + if GetLastError() == ERROR_OPERATION_ABORTED: + # wait for KeyboardInterrupt + time.sleep(0.1) + if not rv: + raise OSError(f"Windows error: {GetLastError()}") + + if buffer[0] == EOF: + return 0 + return 2 * code_units_read.value + + +class _WindowsConsoleWriter(_WindowsConsoleRawIOBase): + def writable(self) -> t.Literal[True]: + return True + + @staticmethod + def _get_error_message(errno: int) -> str: + if errno == ERROR_SUCCESS: + return "ERROR_SUCCESS" + elif errno == ERROR_NOT_ENOUGH_MEMORY: + return "ERROR_NOT_ENOUGH_MEMORY" + return f"Windows error {errno}" + + def write(self, b: Buffer) -> int: + bytes_to_be_written = len(b) + buf = get_buffer(b) + code_units_to_be_written = min(bytes_to_be_written, MAX_BYTES_WRITTEN) // 2 + code_units_written = c_ulong() + + WriteConsoleW( + HANDLE(self.handle), + buf, + code_units_to_be_written, + byref(code_units_written), + None, + ) + bytes_written = 2 * code_units_written.value + + if bytes_written == 0 and bytes_to_be_written > 0: + raise OSError(self._get_error_message(GetLastError())) + return bytes_written + + +class ConsoleStream: + def __init__(self, text_stream: t.TextIO, byte_stream: t.BinaryIO) -> None: + self._text_stream = text_stream + self.buffer = byte_stream + + @property + def name(self) -> str: + return self.buffer.name + + def write(self, x: t.AnyStr) -> int: + if isinstance(x, str): + return self._text_stream.write(x) + try: + self.flush() + except Exception: + pass + return self.buffer.write(x) + + def writelines(self, lines: cabc.Iterable[t.AnyStr]) -> None: + for line in lines: + self.write(line) + + def __getattr__(self, name: str) -> t.Any: + return getattr(self._text_stream, name) + + def isatty(self) -> bool: + return self.buffer.isatty() + + def __repr__(self) -> str: + return f"" + + +def _get_text_stdin(buffer_stream: t.BinaryIO) -> t.TextIO: + text_stream = _NonClosingTextIOWrapper( + io.BufferedReader(_WindowsConsoleReader(STDIN_HANDLE)), + "utf-16-le", + "strict", + line_buffering=True, + ) + return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream)) + + +def _get_text_stdout(buffer_stream: t.BinaryIO) -> t.TextIO: + text_stream = _NonClosingTextIOWrapper( + io.BufferedWriter(_WindowsConsoleWriter(STDOUT_HANDLE)), + "utf-16-le", + "strict", + line_buffering=True, + ) + return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream)) + + +def _get_text_stderr(buffer_stream: t.BinaryIO) -> t.TextIO: + text_stream = _NonClosingTextIOWrapper( + io.BufferedWriter(_WindowsConsoleWriter(STDERR_HANDLE)), + "utf-16-le", + "strict", + line_buffering=True, + ) + return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream)) + + +_stream_factories: cabc.Mapping[int, t.Callable[[t.BinaryIO], t.TextIO]] = { + 0: _get_text_stdin, + 1: _get_text_stdout, + 2: _get_text_stderr, +} + + +def _is_console(f: t.TextIO) -> bool: + if not hasattr(f, "fileno"): + return False + + try: + fileno = f.fileno() + except (OSError, io.UnsupportedOperation): + return False + + handle = msvcrt.get_osfhandle(fileno) + return bool(GetConsoleMode(handle, byref(DWORD()))) + + +def _get_windows_console_stream( + f: t.TextIO, encoding: str | None, errors: str | None +) -> t.TextIO | None: + if ( + get_buffer is None + or encoding not in {"utf-16-le", None} + or errors not in {"strict", None} + or not _is_console(f) + ): + return None + + func = _stream_factories.get(f.fileno()) + if func is None: + return None + + b = getattr(f, "buffer", None) + + if b is None: + return None + + return func(b) diff --git a/venv/lib/python3.12/site-packages/click/core.py b/venv/lib/python3.12/site-packages/click/core.py new file mode 100644 index 00000000..f57ada62 --- /dev/null +++ b/venv/lib/python3.12/site-packages/click/core.py @@ -0,0 +1,3135 @@ +from __future__ import annotations + +import collections.abc as cabc +import enum +import errno +import inspect +import os +import sys +import typing as t +from collections import abc +from collections import Counter +from contextlib import AbstractContextManager +from contextlib import contextmanager +from contextlib import ExitStack +from functools import update_wrapper +from gettext import gettext as _ +from gettext import ngettext +from itertools import repeat +from types import TracebackType + +from . import types +from .exceptions import Abort +from .exceptions import BadParameter +from .exceptions import ClickException +from .exceptions import Exit +from .exceptions import MissingParameter +from .exceptions import NoArgsIsHelpError +from .exceptions import UsageError +from .formatting import HelpFormatter +from .formatting import join_options +from .globals import pop_context +from .globals import push_context +from .parser import _flag_needs_value +from .parser import _OptionParser +from .parser import _split_opt +from .termui import confirm +from .termui import prompt +from .termui import style +from .utils import _detect_program_name +from .utils import _expand_args +from .utils import echo +from .utils import make_default_short_help +from .utils import make_str +from .utils import PacifyFlushWrapper + +if t.TYPE_CHECKING: + from .shell_completion import CompletionItem + +F = t.TypeVar("F", bound="t.Callable[..., t.Any]") +V = t.TypeVar("V") + + +def _complete_visible_commands( + ctx: Context, incomplete: str +) -> cabc.Iterator[tuple[str, Command]]: + """List all the subcommands of a group that start with the + incomplete value and aren't hidden. + + :param ctx: Invocation context for the group. + :param incomplete: Value being completed. May be empty. + """ + multi = t.cast(Group, ctx.command) + + for name in multi.list_commands(ctx): + if name.startswith(incomplete): + command = multi.get_command(ctx, name) + + if command is not None and not command.hidden: + yield name, command + + +def _check_nested_chain( + base_command: Group, cmd_name: str, cmd: Command, register: bool = False +) -> None: + if not base_command.chain or not isinstance(cmd, Group): + return + + if register: + message = ( + f"It is not possible to add the group {cmd_name!r} to another" + f" group {base_command.name!r} that is in chain mode." + ) + else: + message = ( + f"Found the group {cmd_name!r} as subcommand to another group " + f" {base_command.name!r} that is in chain mode. This is not supported." + ) + + raise RuntimeError(message) + + +def batch(iterable: cabc.Iterable[V], batch_size: int) -> list[tuple[V, ...]]: + return list(zip(*repeat(iter(iterable), batch_size), strict=False)) + + +@contextmanager +def augment_usage_errors( + ctx: Context, param: Parameter | None = None +) -> cabc.Iterator[None]: + """Context manager that attaches extra information to exceptions.""" + try: + yield + except BadParameter as e: + if e.ctx is None: + e.ctx = ctx + if param is not None and e.param is None: + e.param = param + raise + except UsageError as e: + if e.ctx is None: + e.ctx = ctx + raise + + +def iter_params_for_processing( + invocation_order: cabc.Sequence[Parameter], + declaration_order: cabc.Sequence[Parameter], +) -> list[Parameter]: + """Returns all declared parameters in the order they should be processed. + + The declared parameters are re-shuffled depending on the order in which + they were invoked, as well as the eagerness of each parameters. + + The invocation order takes precedence over the declaration order. I.e. the + order in which the user provided them to the CLI is respected. + + This behavior and its effect on callback evaluation is detailed at: + https://click.palletsprojects.com/en/stable/advanced/#callback-evaluation-order + """ + + def sort_key(item: Parameter) -> tuple[bool, float]: + try: + idx: float = invocation_order.index(item) + except ValueError: + idx = float("inf") + + return not item.is_eager, idx + + return sorted(declaration_order, key=sort_key) + + +class ParameterSource(enum.Enum): + """This is an :class:`~enum.Enum` that indicates the source of a + parameter's value. + + Use :meth:`click.Context.get_parameter_source` to get the + source for a parameter by name. + + .. versionchanged:: 8.0 + Use :class:`~enum.Enum` and drop the ``validate`` method. + + .. versionchanged:: 8.0 + Added the ``PROMPT`` value. + """ + + COMMANDLINE = enum.auto() + """The value was provided by the command line args.""" + ENVIRONMENT = enum.auto() + """The value was provided with an environment variable.""" + DEFAULT = enum.auto() + """Used the default specified by the parameter.""" + DEFAULT_MAP = enum.auto() + """Used a default provided by :attr:`Context.default_map`.""" + PROMPT = enum.auto() + """Used a prompt to confirm a default or provide a value.""" + + +class Context: + """The context is a special internal object that holds state relevant + for the script execution at every single level. It's normally invisible + to commands unless they opt-in to getting access to it. + + The context is useful as it can pass internal objects around and can + control special execution features such as reading data from + environment variables. + + A context can be used as context manager in which case it will call + :meth:`close` on teardown. + + :param command: the command class for this context. + :param parent: the parent context. + :param info_name: the info name for this invocation. Generally this + is the most descriptive name for the script or + command. For the toplevel script it is usually + the name of the script, for commands below it it's + the name of the script. + :param obj: an arbitrary object of user data. + :param auto_envvar_prefix: the prefix to use for automatic environment + variables. If this is `None` then reading + from environment variables is disabled. This + does not affect manually set environment + variables which are always read. + :param default_map: a dictionary (like object) with default values + for parameters. + :param terminal_width: the width of the terminal. The default is + inherit from parent context. If no context + defines the terminal width then auto + detection will be applied. + :param max_content_width: the maximum width for content rendered by + Click (this currently only affects help + pages). This defaults to 80 characters if + not overridden. In other words: even if the + terminal is larger than that, Click will not + format things wider than 80 characters by + default. In addition to that, formatters might + add some safety mapping on the right. + :param resilient_parsing: if this flag is enabled then Click will + parse without any interactivity or callback + invocation. Default values will also be + ignored. This is useful for implementing + things such as completion support. + :param allow_extra_args: if this is set to `True` then extra arguments + at the end will not raise an error and will be + kept on the context. The default is to inherit + from the command. + :param allow_interspersed_args: if this is set to `False` then options + and arguments cannot be mixed. The + default is to inherit from the command. + :param ignore_unknown_options: instructs click to ignore options it does + not know and keeps them for later + processing. + :param help_option_names: optionally a list of strings that define how + the default help parameter is named. The + default is ``['--help']``. + :param token_normalize_func: an optional function that is used to + normalize tokens (options, choices, + etc.). This for instance can be used to + implement case insensitive behavior. + :param color: controls if the terminal supports ANSI colors or not. The + default is autodetection. This is only needed if ANSI + codes are used in texts that Click prints which is by + default not the case. This for instance would affect + help output. + :param show_default: Show the default value for commands. If this + value is not set, it defaults to the value from the parent + context. ``Command.show_default`` overrides this default for the + specific command. + + .. versionchanged:: 8.2 + The ``protected_args`` attribute is deprecated and will be removed in + Click 9.0. ``args`` will contain remaining unparsed tokens. + + .. versionchanged:: 8.1 + The ``show_default`` parameter is overridden by + ``Command.show_default``, instead of the other way around. + + .. versionchanged:: 8.0 + The ``show_default`` parameter defaults to the value from the + parent context. + + .. versionchanged:: 7.1 + Added the ``show_default`` parameter. + + .. versionchanged:: 4.0 + Added the ``color``, ``ignore_unknown_options``, and + ``max_content_width`` parameters. + + .. versionchanged:: 3.0 + Added the ``allow_extra_args`` and ``allow_interspersed_args`` + parameters. + + .. versionchanged:: 2.0 + Added the ``resilient_parsing``, ``help_option_names``, and + ``token_normalize_func`` parameters. + """ + + #: The formatter class to create with :meth:`make_formatter`. + #: + #: .. versionadded:: 8.0 + formatter_class: type[HelpFormatter] = HelpFormatter + + def __init__( + self, + command: Command, + parent: Context | None = None, + info_name: str | None = None, + obj: t.Any | None = None, + auto_envvar_prefix: str | None = None, + default_map: cabc.MutableMapping[str, t.Any] | None = None, + terminal_width: int | None = None, + max_content_width: int | None = None, + resilient_parsing: bool = False, + allow_extra_args: bool | None = None, + allow_interspersed_args: bool | None = None, + ignore_unknown_options: bool | None = None, + help_option_names: list[str] | None = None, + token_normalize_func: t.Callable[[str], str] | None = None, + color: bool | None = None, + show_default: bool | None = None, + ) -> None: + #: the parent context or `None` if none exists. + self.parent = parent + #: the :class:`Command` for this context. + self.command = command + #: the descriptive information name + self.info_name = info_name + #: Map of parameter names to their parsed values. Parameters + #: with ``expose_value=False`` are not stored. + self.params: dict[str, t.Any] = {} + #: the leftover arguments. + self.args: list[str] = [] + #: protected arguments. These are arguments that are prepended + #: to `args` when certain parsing scenarios are encountered but + #: must be never propagated to another arguments. This is used + #: to implement nested parsing. + self._protected_args: list[str] = [] + #: the collected prefixes of the command's options. + self._opt_prefixes: set[str] = set(parent._opt_prefixes) if parent else set() + + if obj is None and parent is not None: + obj = parent.obj + + #: the user object stored. + self.obj: t.Any = obj + self._meta: dict[str, t.Any] = getattr(parent, "meta", {}) + + #: A dictionary (-like object) with defaults for parameters. + if ( + default_map is None + and info_name is not None + and parent is not None + and parent.default_map is not None + ): + default_map = parent.default_map.get(info_name) + + self.default_map: cabc.MutableMapping[str, t.Any] | None = default_map + + #: This flag indicates if a subcommand is going to be executed. A + #: group callback can use this information to figure out if it's + #: being executed directly or because the execution flow passes + #: onwards to a subcommand. By default it's None, but it can be + #: the name of the subcommand to execute. + #: + #: If chaining is enabled this will be set to ``'*'`` in case + #: any commands are executed. It is however not possible to + #: figure out which ones. If you require this knowledge you + #: should use a :func:`result_callback`. + self.invoked_subcommand: str | None = None + + if terminal_width is None and parent is not None: + terminal_width = parent.terminal_width + + #: The width of the terminal (None is autodetection). + self.terminal_width: int | None = terminal_width + + if max_content_width is None and parent is not None: + max_content_width = parent.max_content_width + + #: The maximum width of formatted content (None implies a sensible + #: default which is 80 for most things). + self.max_content_width: int | None = max_content_width + + if allow_extra_args is None: + allow_extra_args = command.allow_extra_args + + #: Indicates if the context allows extra args or if it should + #: fail on parsing. + #: + #: .. versionadded:: 3.0 + self.allow_extra_args = allow_extra_args + + if allow_interspersed_args is None: + allow_interspersed_args = command.allow_interspersed_args + + #: Indicates if the context allows mixing of arguments and + #: options or not. + #: + #: .. versionadded:: 3.0 + self.allow_interspersed_args: bool = allow_interspersed_args + + if ignore_unknown_options is None: + ignore_unknown_options = command.ignore_unknown_options + + #: Instructs click to ignore options that a command does not + #: understand and will store it on the context for later + #: processing. This is primarily useful for situations where you + #: want to call into external programs. Generally this pattern is + #: strongly discouraged because it's not possibly to losslessly + #: forward all arguments. + #: + #: .. versionadded:: 4.0 + self.ignore_unknown_options: bool = ignore_unknown_options + + if help_option_names is None: + if parent is not None: + help_option_names = parent.help_option_names + else: + help_option_names = ["--help"] + + #: The names for the help options. + self.help_option_names: list[str] = help_option_names + + if token_normalize_func is None and parent is not None: + token_normalize_func = parent.token_normalize_func + + #: An optional normalization function for tokens. This is + #: options, choices, commands etc. + self.token_normalize_func: t.Callable[[str], str] | None = token_normalize_func + + #: Indicates if resilient parsing is enabled. In that case Click + #: will do its best to not cause any failures and default values + #: will be ignored. Useful for completion. + self.resilient_parsing: bool = resilient_parsing + + # If there is no envvar prefix yet, but the parent has one and + # the command on this level has a name, we can expand the envvar + # prefix automatically. + if auto_envvar_prefix is None: + if ( + parent is not None + and parent.auto_envvar_prefix is not None + and self.info_name is not None + ): + auto_envvar_prefix = ( + f"{parent.auto_envvar_prefix}_{self.info_name.upper()}" + ) + else: + auto_envvar_prefix = auto_envvar_prefix.upper() + + if auto_envvar_prefix is not None: + auto_envvar_prefix = auto_envvar_prefix.replace("-", "_") + + self.auto_envvar_prefix: str | None = auto_envvar_prefix + + if color is None and parent is not None: + color = parent.color + + #: Controls if styling output is wanted or not. + self.color: bool | None = color + + if show_default is None and parent is not None: + show_default = parent.show_default + + #: Show option default values when formatting help text. + self.show_default: bool | None = show_default + + self._close_callbacks: list[t.Callable[[], t.Any]] = [] + self._depth = 0 + self._parameter_source: dict[str, ParameterSource] = {} + self._exit_stack = ExitStack() + + @property + def protected_args(self) -> list[str]: + import warnings + + warnings.warn( + "'protected_args' is deprecated and will be removed in Click 9.0." + " 'args' will contain remaining unparsed tokens.", + DeprecationWarning, + stacklevel=2, + ) + return self._protected_args + + def to_info_dict(self) -> dict[str, t.Any]: + """Gather information that could be useful for a tool generating + user-facing documentation. This traverses the entire CLI + structure. + + .. code-block:: python + + with Context(cli) as ctx: + info = ctx.to_info_dict() + + .. versionadded:: 8.0 + """ + return { + "command": self.command.to_info_dict(self), + "info_name": self.info_name, + "allow_extra_args": self.allow_extra_args, + "allow_interspersed_args": self.allow_interspersed_args, + "ignore_unknown_options": self.ignore_unknown_options, + "auto_envvar_prefix": self.auto_envvar_prefix, + } + + def __enter__(self) -> Context: + self._depth += 1 + push_context(self) + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + tb: TracebackType | None, + ) -> None: + self._depth -= 1 + if self._depth == 0: + self.close() + pop_context() + + @contextmanager + def scope(self, cleanup: bool = True) -> cabc.Iterator[Context]: + """This helper method can be used with the context object to promote + it to the current thread local (see :func:`get_current_context`). + The default behavior of this is to invoke the cleanup functions which + can be disabled by setting `cleanup` to `False`. The cleanup + functions are typically used for things such as closing file handles. + + If the cleanup is intended the context object can also be directly + used as a context manager. + + Example usage:: + + with ctx.scope(): + assert get_current_context() is ctx + + This is equivalent:: + + with ctx: + assert get_current_context() is ctx + + .. versionadded:: 5.0 + + :param cleanup: controls if the cleanup functions should be run or + not. The default is to run these functions. In + some situations the context only wants to be + temporarily pushed in which case this can be disabled. + Nested pushes automatically defer the cleanup. + """ + if not cleanup: + self._depth += 1 + try: + with self as rv: + yield rv + finally: + if not cleanup: + self._depth -= 1 + + @property + def meta(self) -> dict[str, t.Any]: + """This is a dictionary which is shared with all the contexts + that are nested. It exists so that click utilities can store some + state here if they need to. It is however the responsibility of + that code to manage this dictionary well. + + The keys are supposed to be unique dotted strings. For instance + module paths are a good choice for it. What is stored in there is + irrelevant for the operation of click. However what is important is + that code that places data here adheres to the general semantics of + the system. + + Example usage:: + + LANG_KEY = f'{__name__}.lang' + + def set_language(value): + ctx = get_current_context() + ctx.meta[LANG_KEY] = value + + def get_language(): + return get_current_context().meta.get(LANG_KEY, 'en_US') + + .. versionadded:: 5.0 + """ + return self._meta + + def make_formatter(self) -> HelpFormatter: + """Creates the :class:`~click.HelpFormatter` for the help and + usage output. + + To quickly customize the formatter class used without overriding + this method, set the :attr:`formatter_class` attribute. + + .. versionchanged:: 8.0 + Added the :attr:`formatter_class` attribute. + """ + return self.formatter_class( + width=self.terminal_width, max_width=self.max_content_width + ) + + def with_resource(self, context_manager: AbstractContextManager[V]) -> V: + """Register a resource as if it were used in a ``with`` + statement. The resource will be cleaned up when the context is + popped. + + Uses :meth:`contextlib.ExitStack.enter_context`. It calls the + resource's ``__enter__()`` method and returns the result. When + the context is popped, it closes the stack, which calls the + resource's ``__exit__()`` method. + + To register a cleanup function for something that isn't a + context manager, use :meth:`call_on_close`. Or use something + from :mod:`contextlib` to turn it into a context manager first. + + .. code-block:: python + + @click.group() + @click.option("--name") + @click.pass_context + def cli(ctx): + ctx.obj = ctx.with_resource(connect_db(name)) + + :param context_manager: The context manager to enter. + :return: Whatever ``context_manager.__enter__()`` returns. + + .. versionadded:: 8.0 + """ + return self._exit_stack.enter_context(context_manager) + + def call_on_close(self, f: t.Callable[..., t.Any]) -> t.Callable[..., t.Any]: + """Register a function to be called when the context tears down. + + This can be used to close resources opened during the script + execution. Resources that support Python's context manager + protocol which would be used in a ``with`` statement should be + registered with :meth:`with_resource` instead. + + :param f: The function to execute on teardown. + """ + return self._exit_stack.callback(f) + + def close(self) -> None: + """Invoke all close callbacks registered with + :meth:`call_on_close`, and exit all context managers entered + with :meth:`with_resource`. + """ + self._exit_stack.close() + # In case the context is reused, create a new exit stack. + self._exit_stack = ExitStack() + + @property + def command_path(self) -> str: + """The computed command path. This is used for the ``usage`` + information on the help page. It's automatically created by + combining the info names of the chain of contexts to the root. + """ + rv = "" + if self.info_name is not None: + rv = self.info_name + if self.parent is not None: + parent_command_path = [self.parent.command_path] + + if isinstance(self.parent.command, Command): + for param in self.parent.command.get_params(self): + parent_command_path.extend(param.get_usage_pieces(self)) + + rv = f"{' '.join(parent_command_path)} {rv}" + return rv.lstrip() + + def find_root(self) -> Context: + """Finds the outermost context.""" + node = self + while node.parent is not None: + node = node.parent + return node + + def find_object(self, object_type: type[V]) -> V | None: + """Finds the closest object of a given type.""" + node: Context | None = self + + while node is not None: + if isinstance(node.obj, object_type): + return node.obj + + node = node.parent + + return None + + def ensure_object(self, object_type: type[V]) -> V: + """Like :meth:`find_object` but sets the innermost object to a + new instance of `object_type` if it does not exist. + """ + rv = self.find_object(object_type) + if rv is None: + self.obj = rv = object_type() + return rv + + @t.overload + def lookup_default( + self, name: str, call: t.Literal[True] = True + ) -> t.Any | None: ... + + @t.overload + def lookup_default( + self, name: str, call: t.Literal[False] = ... + ) -> t.Any | t.Callable[[], t.Any] | None: ... + + def lookup_default(self, name: str, call: bool = True) -> t.Any | None: + """Get the default for a parameter from :attr:`default_map`. + + :param name: Name of the parameter. + :param call: If the default is a callable, call it. Disable to + return the callable instead. + + .. versionchanged:: 8.0 + Added the ``call`` parameter. + """ + if self.default_map is not None: + value = self.default_map.get(name) + + if call and callable(value): + return value() + + return value + + return None + + def fail(self, message: str) -> t.NoReturn: + """Aborts the execution of the program with a specific error + message. + + :param message: the error message to fail with. + """ + raise UsageError(message, self) + + def abort(self) -> t.NoReturn: + """Aborts the script.""" + raise Abort() + + def exit(self, code: int = 0) -> t.NoReturn: + """Exits the application with a given exit code. + + .. versionchanged:: 8.2 + Callbacks and context managers registered with :meth:`call_on_close` + and :meth:`with_resource` are closed before exiting. + """ + self.close() + raise Exit(code) + + def get_usage(self) -> str: + """Helper method to get formatted usage string for the current + context and command. + """ + return self.command.get_usage(self) + + def get_help(self) -> str: + """Helper method to get formatted help page for the current + context and command. + """ + return self.command.get_help(self) + + def _make_sub_context(self, command: Command) -> Context: + """Create a new context of the same type as this context, but + for a new command. + + :meta private: + """ + return type(self)(command, info_name=command.name, parent=self) + + @t.overload + def invoke( + self, callback: t.Callable[..., V], /, *args: t.Any, **kwargs: t.Any + ) -> V: ... + + @t.overload + def invoke(self, callback: Command, /, *args: t.Any, **kwargs: t.Any) -> t.Any: ... + + def invoke( + self, callback: Command | t.Callable[..., V], /, *args: t.Any, **kwargs: t.Any + ) -> t.Any | V: + """Invokes a command callback in exactly the way it expects. There + are two ways to invoke this method: + + 1. the first argument can be a callback and all other arguments and + keyword arguments are forwarded directly to the function. + 2. the first argument is a click command object. In that case all + arguments are forwarded as well but proper click parameters + (options and click arguments) must be keyword arguments and Click + will fill in defaults. + + .. versionchanged:: 8.0 + All ``kwargs`` are tracked in :attr:`params` so they will be + passed if :meth:`forward` is called at multiple levels. + + .. versionchanged:: 3.2 + A new context is created, and missing arguments use default values. + """ + if isinstance(callback, Command): + other_cmd = callback + + if other_cmd.callback is None: + raise TypeError( + "The given command does not have a callback that can be invoked." + ) + else: + callback = t.cast("t.Callable[..., V]", other_cmd.callback) + + ctx = self._make_sub_context(other_cmd) + + for param in other_cmd.params: + if param.name not in kwargs and param.expose_value: + kwargs[param.name] = param.type_cast_value( # type: ignore + ctx, param.get_default(ctx) + ) + + # Track all kwargs as params, so that forward() will pass + # them on in subsequent calls. + ctx.params.update(kwargs) + else: + ctx = self + + with augment_usage_errors(self): + with ctx: + return callback(*args, **kwargs) + + def forward(self, cmd: Command, /, *args: t.Any, **kwargs: t.Any) -> t.Any: + """Similar to :meth:`invoke` but fills in default keyword + arguments from the current context if the other command expects + it. This cannot invoke callbacks directly, only other commands. + + .. versionchanged:: 8.0 + All ``kwargs`` are tracked in :attr:`params` so they will be + passed if ``forward`` is called at multiple levels. + """ + # Can only forward to other commands, not direct callbacks. + if not isinstance(cmd, Command): + raise TypeError("Callback is not a command.") + + for param in self.params: + if param not in kwargs: + kwargs[param] = self.params[param] + + return self.invoke(cmd, *args, **kwargs) + + def set_parameter_source(self, name: str, source: ParameterSource) -> None: + """Set the source of a parameter. This indicates the location + from which the value of the parameter was obtained. + + :param name: The name of the parameter. + :param source: A member of :class:`~click.core.ParameterSource`. + """ + self._parameter_source[name] = source + + def get_parameter_source(self, name: str) -> ParameterSource | None: + """Get the source of a parameter. This indicates the location + from which the value of the parameter was obtained. + + This can be useful for determining when a user specified a value + on the command line that is the same as the default value. It + will be :attr:`~click.core.ParameterSource.DEFAULT` only if the + value was actually taken from the default. + + :param name: The name of the parameter. + :rtype: ParameterSource + + .. versionchanged:: 8.0 + Returns ``None`` if the parameter was not provided from any + source. + """ + return self._parameter_source.get(name) + + +class Command: + """Commands are the basic building block of command line interfaces in + Click. A basic command handles command line parsing and might dispatch + more parsing to commands nested below it. + + :param name: the name of the command to use unless a group overrides it. + :param context_settings: an optional dictionary with defaults that are + passed to the context object. + :param callback: the callback to invoke. This is optional. + :param params: the parameters to register with this command. This can + be either :class:`Option` or :class:`Argument` objects. + :param help: the help string to use for this command. + :param epilog: like the help string but it's printed at the end of the + help page after everything else. + :param short_help: the short help to use for this command. This is + shown on the command listing of the parent command. + :param add_help_option: by default each command registers a ``--help`` + option. This can be disabled by this parameter. + :param no_args_is_help: this controls what happens if no arguments are + provided. This option is disabled by default. + If enabled this will add ``--help`` as argument + if no arguments are passed + :param hidden: hide this command from help outputs. + :param deprecated: If ``True`` or non-empty string, issues a message + indicating that the command is deprecated and highlights + its deprecation in --help. The message can be customized + by using a string as the value. + + .. versionchanged:: 8.2 + This is the base class for all commands, not ``BaseCommand``. + ``deprecated`` can be set to a string as well to customize the + deprecation message. + + .. versionchanged:: 8.1 + ``help``, ``epilog``, and ``short_help`` are stored unprocessed, + all formatting is done when outputting help text, not at init, + and is done even if not using the ``@command`` decorator. + + .. versionchanged:: 8.0 + Added a ``repr`` showing the command name. + + .. versionchanged:: 7.1 + Added the ``no_args_is_help`` parameter. + + .. versionchanged:: 2.0 + Added the ``context_settings`` parameter. + """ + + #: The context class to create with :meth:`make_context`. + #: + #: .. versionadded:: 8.0 + context_class: type[Context] = Context + + #: the default for the :attr:`Context.allow_extra_args` flag. + allow_extra_args = False + + #: the default for the :attr:`Context.allow_interspersed_args` flag. + allow_interspersed_args = True + + #: the default for the :attr:`Context.ignore_unknown_options` flag. + ignore_unknown_options = False + + def __init__( + self, + name: str | None, + context_settings: cabc.MutableMapping[str, t.Any] | None = None, + callback: t.Callable[..., t.Any] | None = None, + params: list[Parameter] | None = None, + help: str | None = None, + epilog: str | None = None, + short_help: str | None = None, + options_metavar: str | None = "[OPTIONS]", + add_help_option: bool = True, + no_args_is_help: bool = False, + hidden: bool = False, + deprecated: bool | str = False, + ) -> None: + #: the name the command thinks it has. Upon registering a command + #: on a :class:`Group` the group will default the command name + #: with this information. You should instead use the + #: :class:`Context`\'s :attr:`~Context.info_name` attribute. + self.name = name + + if context_settings is None: + context_settings = {} + + #: an optional dictionary with defaults passed to the context. + self.context_settings: cabc.MutableMapping[str, t.Any] = context_settings + + #: the callback to execute when the command fires. This might be + #: `None` in which case nothing happens. + self.callback = callback + #: the list of parameters for this command in the order they + #: should show up in the help page and execute. Eager parameters + #: will automatically be handled before non eager ones. + self.params: list[Parameter] = params or [] + self.help = help + self.epilog = epilog + self.options_metavar = options_metavar + self.short_help = short_help + self.add_help_option = add_help_option + self._help_option = None + self.no_args_is_help = no_args_is_help + self.hidden = hidden + self.deprecated = deprecated + + def to_info_dict(self, ctx: Context) -> dict[str, t.Any]: + return { + "name": self.name, + "params": [param.to_info_dict() for param in self.get_params(ctx)], + "help": self.help, + "epilog": self.epilog, + "short_help": self.short_help, + "hidden": self.hidden, + "deprecated": self.deprecated, + } + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} {self.name}>" + + def get_usage(self, ctx: Context) -> str: + """Formats the usage line into a string and returns it. + + Calls :meth:`format_usage` internally. + """ + formatter = ctx.make_formatter() + self.format_usage(ctx, formatter) + return formatter.getvalue().rstrip("\n") + + def get_params(self, ctx: Context) -> list[Parameter]: + params = self.params + help_option = self.get_help_option(ctx) + + if help_option is not None: + params = [*params, help_option] + + if __debug__: + import warnings + + opts = [opt for param in params for opt in param.opts] + opts_counter = Counter(opts) + duplicate_opts = (opt for opt, count in opts_counter.items() if count > 1) + + for duplicate_opt in duplicate_opts: + warnings.warn( + ( + f"The parameter {duplicate_opt} is used more than once. " + "Remove its duplicate as parameters should be unique." + ), + stacklevel=3, + ) + + return params + + def format_usage(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the usage line into the formatter. + + This is a low-level method called by :meth:`get_usage`. + """ + pieces = self.collect_usage_pieces(ctx) + formatter.write_usage(ctx.command_path, " ".join(pieces)) + + def collect_usage_pieces(self, ctx: Context) -> list[str]: + """Returns all the pieces that go into the usage line and returns + it as a list of strings. + """ + rv = [self.options_metavar] if self.options_metavar else [] + + for param in self.get_params(ctx): + rv.extend(param.get_usage_pieces(ctx)) + + return rv + + def get_help_option_names(self, ctx: Context) -> list[str]: + """Returns the names for the help option.""" + all_names = set(ctx.help_option_names) + for param in self.params: + all_names.difference_update(param.opts) + all_names.difference_update(param.secondary_opts) + return list(all_names) + + def get_help_option(self, ctx: Context) -> Option | None: + """Returns the help option object. + + Skipped if :attr:`add_help_option` is ``False``. + + .. versionchanged:: 8.1.8 + The help option is now cached to avoid creating it multiple times. + """ + help_option_names = self.get_help_option_names(ctx) + + if not help_option_names or not self.add_help_option: + return None + + # Cache the help option object in private _help_option attribute to + # avoid creating it multiple times. Not doing this will break the + # callback odering by iter_params_for_processing(), which relies on + # object comparison. + if self._help_option is None: + # Avoid circular import. + from .decorators import help_option + + # Apply help_option decorator and pop resulting option + help_option(*help_option_names)(self) + self._help_option = self.params.pop() # type: ignore[assignment] + + return self._help_option + + def make_parser(self, ctx: Context) -> _OptionParser: + """Creates the underlying option parser for this command.""" + parser = _OptionParser(ctx) + for param in self.get_params(ctx): + param.add_to_parser(parser, ctx) + return parser + + def get_help(self, ctx: Context) -> str: + """Formats the help into a string and returns it. + + Calls :meth:`format_help` internally. + """ + formatter = ctx.make_formatter() + self.format_help(ctx, formatter) + return formatter.getvalue().rstrip("\n") + + def get_short_help_str(self, limit: int = 45) -> str: + """Gets short help for the command or makes it by shortening the + long help string. + """ + if self.short_help: + text = inspect.cleandoc(self.short_help) + elif self.help: + text = make_default_short_help(self.help, limit) + else: + text = "" + + if self.deprecated: + deprecated_message = ( + f"(DEPRECATED: {self.deprecated})" + if isinstance(self.deprecated, str) + else "(DEPRECATED)" + ) + text = _("{text} {deprecated_message}").format( + text=text, deprecated_message=deprecated_message + ) + + return text.strip() + + def format_help(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the help into the formatter if it exists. + + This is a low-level method called by :meth:`get_help`. + + This calls the following methods: + + - :meth:`format_usage` + - :meth:`format_help_text` + - :meth:`format_options` + - :meth:`format_epilog` + """ + self.format_usage(ctx, formatter) + self.format_help_text(ctx, formatter) + self.format_options(ctx, formatter) + self.format_epilog(ctx, formatter) + + def format_help_text(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the help text to the formatter if it exists.""" + if self.help is not None: + # truncate the help text to the first form feed + text = inspect.cleandoc(self.help).partition("\f")[0] + else: + text = "" + + if self.deprecated: + deprecated_message = ( + f"(DEPRECATED: {self.deprecated})" + if isinstance(self.deprecated, str) + else "(DEPRECATED)" + ) + text = _("{text} {deprecated_message}").format( + text=text, deprecated_message=deprecated_message + ) + + if text: + formatter.write_paragraph() + + with formatter.indentation(): + formatter.write_text(text) + + def format_options(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes all the options into the formatter if they exist.""" + opts = [] + for param in self.get_params(ctx): + rv = param.get_help_record(ctx) + if rv is not None: + opts.append(rv) + + if opts: + with formatter.section(_("Options")): + formatter.write_dl(opts) + + def format_epilog(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the epilog into the formatter if it exists.""" + if self.epilog: + epilog = inspect.cleandoc(self.epilog) + formatter.write_paragraph() + + with formatter.indentation(): + formatter.write_text(epilog) + + def make_context( + self, + info_name: str | None, + args: list[str], + parent: Context | None = None, + **extra: t.Any, + ) -> Context: + """This function when given an info name and arguments will kick + off the parsing and create a new :class:`Context`. It does not + invoke the actual command callback though. + + To quickly customize the context class used without overriding + this method, set the :attr:`context_class` attribute. + + :param info_name: the info name for this invocation. Generally this + is the most descriptive name for the script or + command. For the toplevel script it's usually + the name of the script, for commands below it's + the name of the command. + :param args: the arguments to parse as list of strings. + :param parent: the parent context if available. + :param extra: extra keyword arguments forwarded to the context + constructor. + + .. versionchanged:: 8.0 + Added the :attr:`context_class` attribute. + """ + for key, value in self.context_settings.items(): + if key not in extra: + extra[key] = value + + ctx = self.context_class(self, info_name=info_name, parent=parent, **extra) + + with ctx.scope(cleanup=False): + self.parse_args(ctx, args) + return ctx + + def parse_args(self, ctx: Context, args: list[str]) -> list[str]: + if not args and self.no_args_is_help and not ctx.resilient_parsing: + raise NoArgsIsHelpError(ctx) + + parser = self.make_parser(ctx) + opts, args, param_order = parser.parse_args(args=args) + + for param in iter_params_for_processing(param_order, self.get_params(ctx)): + value, args = param.handle_parse_result(ctx, opts, args) + + if args and not ctx.allow_extra_args and not ctx.resilient_parsing: + ctx.fail( + ngettext( + "Got unexpected extra argument ({args})", + "Got unexpected extra arguments ({args})", + len(args), + ).format(args=" ".join(map(str, args))) + ) + + ctx.args = args + ctx._opt_prefixes.update(parser._opt_prefixes) + return args + + def invoke(self, ctx: Context) -> t.Any: + """Given a context, this invokes the attached callback (if it exists) + in the right way. + """ + if self.deprecated: + extra_message = ( + f" {self.deprecated}" if isinstance(self.deprecated, str) else "" + ) + message = _( + "DeprecationWarning: The command {name!r} is deprecated.{extra_message}" + ).format(name=self.name, extra_message=extra_message) + echo(style(message, fg="red"), err=True) + + if self.callback is not None: + return ctx.invoke(self.callback, **ctx.params) + + def shell_complete(self, ctx: Context, incomplete: str) -> list[CompletionItem]: + """Return a list of completions for the incomplete value. Looks + at the names of options and chained multi-commands. + + Any command could be part of a chained multi-command, so sibling + commands are valid at any point during command completion. + + :param ctx: Invocation context for this command. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + results: list[CompletionItem] = [] + + if incomplete and not incomplete[0].isalnum(): + for param in self.get_params(ctx): + if ( + not isinstance(param, Option) + or param.hidden + or ( + not param.multiple + and ctx.get_parameter_source(param.name) # type: ignore + is ParameterSource.COMMANDLINE + ) + ): + continue + + results.extend( + CompletionItem(name, help=param.help) + for name in [*param.opts, *param.secondary_opts] + if name.startswith(incomplete) + ) + + while ctx.parent is not None: + ctx = ctx.parent + + if isinstance(ctx.command, Group) and ctx.command.chain: + results.extend( + CompletionItem(name, help=command.get_short_help_str()) + for name, command in _complete_visible_commands(ctx, incomplete) + if name not in ctx._protected_args + ) + + return results + + @t.overload + def main( + self, + args: cabc.Sequence[str] | None = None, + prog_name: str | None = None, + complete_var: str | None = None, + standalone_mode: t.Literal[True] = True, + **extra: t.Any, + ) -> t.NoReturn: ... + + @t.overload + def main( + self, + args: cabc.Sequence[str] | None = None, + prog_name: str | None = None, + complete_var: str | None = None, + standalone_mode: bool = ..., + **extra: t.Any, + ) -> t.Any: ... + + def main( + self, + args: cabc.Sequence[str] | None = None, + prog_name: str | None = None, + complete_var: str | None = None, + standalone_mode: bool = True, + windows_expand_args: bool = True, + **extra: t.Any, + ) -> t.Any: + """This is the way to invoke a script with all the bells and + whistles as a command line application. This will always terminate + the application after a call. If this is not wanted, ``SystemExit`` + needs to be caught. + + This method is also available by directly calling the instance of + a :class:`Command`. + + :param args: the arguments that should be used for parsing. If not + provided, ``sys.argv[1:]`` is used. + :param prog_name: the program name that should be used. By default + the program name is constructed by taking the file + name from ``sys.argv[0]``. + :param complete_var: the environment variable that controls the + bash completion support. The default is + ``"__COMPLETE"`` with prog_name in + uppercase. + :param standalone_mode: the default behavior is to invoke the script + in standalone mode. Click will then + handle exceptions and convert them into + error messages and the function will never + return but shut down the interpreter. If + this is set to `False` they will be + propagated to the caller and the return + value of this function is the return value + of :meth:`invoke`. + :param windows_expand_args: Expand glob patterns, user dir, and + env vars in command line args on Windows. + :param extra: extra keyword arguments are forwarded to the context + constructor. See :class:`Context` for more information. + + .. versionchanged:: 8.0.1 + Added the ``windows_expand_args`` parameter to allow + disabling command line arg expansion on Windows. + + .. versionchanged:: 8.0 + When taking arguments from ``sys.argv`` on Windows, glob + patterns, user dir, and env vars are expanded. + + .. versionchanged:: 3.0 + Added the ``standalone_mode`` parameter. + """ + if args is None: + args = sys.argv[1:] + + if os.name == "nt" and windows_expand_args: + args = _expand_args(args) + else: + args = list(args) + + if prog_name is None: + prog_name = _detect_program_name() + + # Process shell completion requests and exit early. + self._main_shell_completion(extra, prog_name, complete_var) + + try: + try: + with self.make_context(prog_name, args, **extra) as ctx: + rv = self.invoke(ctx) + if not standalone_mode: + return rv + # it's not safe to `ctx.exit(rv)` here! + # note that `rv` may actually contain data like "1" which + # has obvious effects + # more subtle case: `rv=[None, None]` can come out of + # chained commands which all returned `None` -- so it's not + # even always obvious that `rv` indicates success/failure + # by its truthiness/falsiness + ctx.exit() + except (EOFError, KeyboardInterrupt) as e: + echo(file=sys.stderr) + raise Abort() from e + except ClickException as e: + if not standalone_mode: + raise + e.show() + sys.exit(e.exit_code) + except OSError as e: + if e.errno == errno.EPIPE: + sys.stdout = t.cast(t.TextIO, PacifyFlushWrapper(sys.stdout)) + sys.stderr = t.cast(t.TextIO, PacifyFlushWrapper(sys.stderr)) + sys.exit(1) + else: + raise + except Exit as e: + if standalone_mode: + sys.exit(e.exit_code) + else: + # in non-standalone mode, return the exit code + # note that this is only reached if `self.invoke` above raises + # an Exit explicitly -- thus bypassing the check there which + # would return its result + # the results of non-standalone execution may therefore be + # somewhat ambiguous: if there are codepaths which lead to + # `ctx.exit(1)` and to `return 1`, the caller won't be able to + # tell the difference between the two + return e.exit_code + except Abort: + if not standalone_mode: + raise + echo(_("Aborted!"), file=sys.stderr) + sys.exit(1) + + def _main_shell_completion( + self, + ctx_args: cabc.MutableMapping[str, t.Any], + prog_name: str, + complete_var: str | None = None, + ) -> None: + """Check if the shell is asking for tab completion, process + that, then exit early. Called from :meth:`main` before the + program is invoked. + + :param prog_name: Name of the executable in the shell. + :param complete_var: Name of the environment variable that holds + the completion instruction. Defaults to + ``_{PROG_NAME}_COMPLETE``. + + .. versionchanged:: 8.2.0 + Dots (``.``) in ``prog_name`` are replaced with underscores (``_``). + """ + if complete_var is None: + complete_name = prog_name.replace("-", "_").replace(".", "_") + complete_var = f"_{complete_name}_COMPLETE".upper() + + instruction = os.environ.get(complete_var) + + if not instruction: + return + + from .shell_completion import shell_complete + + rv = shell_complete(self, ctx_args, prog_name, complete_var, instruction) + sys.exit(rv) + + def __call__(self, *args: t.Any, **kwargs: t.Any) -> t.Any: + """Alias for :meth:`main`.""" + return self.main(*args, **kwargs) + + +class _FakeSubclassCheck(type): + def __subclasscheck__(cls, subclass: type) -> bool: + return issubclass(subclass, cls.__bases__[0]) + + def __instancecheck__(cls, instance: t.Any) -> bool: + return isinstance(instance, cls.__bases__[0]) + + +class _BaseCommand(Command, metaclass=_FakeSubclassCheck): + """ + .. deprecated:: 8.2 + Will be removed in Click 9.0. Use ``Command`` instead. + """ + + +class Group(Command): + """A group is a command that nests other commands (or more groups). + + :param name: The name of the group command. + :param commands: Map names to :class:`Command` objects. Can be a list, which + will use :attr:`Command.name` as the keys. + :param invoke_without_command: Invoke the group's callback even if a + subcommand is not given. + :param no_args_is_help: If no arguments are given, show the group's help and + exit. Defaults to the opposite of ``invoke_without_command``. + :param subcommand_metavar: How to represent the subcommand argument in help. + The default will represent whether ``chain`` is set or not. + :param chain: Allow passing more than one subcommand argument. After parsing + a command's arguments, if any arguments remain another command will be + matched, and so on. + :param result_callback: A function to call after the group's and + subcommand's callbacks. The value returned by the subcommand is passed. + If ``chain`` is enabled, the value will be a list of values returned by + all the commands. If ``invoke_without_command`` is enabled, the value + will be the value returned by the group's callback, or an empty list if + ``chain`` is enabled. + :param kwargs: Other arguments passed to :class:`Command`. + + .. versionchanged:: 8.0 + The ``commands`` argument can be a list of command objects. + + .. versionchanged:: 8.2 + Merged with and replaces the ``MultiCommand`` base class. + """ + + allow_extra_args = True + allow_interspersed_args = False + + #: If set, this is used by the group's :meth:`command` decorator + #: as the default :class:`Command` class. This is useful to make all + #: subcommands use a custom command class. + #: + #: .. versionadded:: 8.0 + command_class: type[Command] | None = None + + #: If set, this is used by the group's :meth:`group` decorator + #: as the default :class:`Group` class. This is useful to make all + #: subgroups use a custom group class. + #: + #: If set to the special value :class:`type` (literally + #: ``group_class = type``), this group's class will be used as the + #: default class. This makes a custom group class continue to make + #: custom groups. + #: + #: .. versionadded:: 8.0 + group_class: type[Group] | type[type] | None = None + # Literal[type] isn't valid, so use Type[type] + + def __init__( + self, + name: str | None = None, + commands: cabc.MutableMapping[str, Command] + | cabc.Sequence[Command] + | None = None, + invoke_without_command: bool = False, + no_args_is_help: bool | None = None, + subcommand_metavar: str | None = None, + chain: bool = False, + result_callback: t.Callable[..., t.Any] | None = None, + **kwargs: t.Any, + ) -> None: + super().__init__(name, **kwargs) + + if commands is None: + commands = {} + elif isinstance(commands, abc.Sequence): + commands = {c.name: c for c in commands if c.name is not None} + + #: The registered subcommands by their exported names. + self.commands: cabc.MutableMapping[str, Command] = commands + + if no_args_is_help is None: + no_args_is_help = not invoke_without_command + + self.no_args_is_help = no_args_is_help + self.invoke_without_command = invoke_without_command + + if subcommand_metavar is None: + if chain: + subcommand_metavar = "COMMAND1 [ARGS]... [COMMAND2 [ARGS]...]..." + else: + subcommand_metavar = "COMMAND [ARGS]..." + + self.subcommand_metavar = subcommand_metavar + self.chain = chain + # The result callback that is stored. This can be set or + # overridden with the :func:`result_callback` decorator. + self._result_callback = result_callback + + if self.chain: + for param in self.params: + if isinstance(param, Argument) and not param.required: + raise RuntimeError( + "A group in chain mode cannot have optional arguments." + ) + + def to_info_dict(self, ctx: Context) -> dict[str, t.Any]: + info_dict = super().to_info_dict(ctx) + commands = {} + + for name in self.list_commands(ctx): + command = self.get_command(ctx, name) + + if command is None: + continue + + sub_ctx = ctx._make_sub_context(command) + + with sub_ctx.scope(cleanup=False): + commands[name] = command.to_info_dict(sub_ctx) + + info_dict.update(commands=commands, chain=self.chain) + return info_dict + + def add_command(self, cmd: Command, name: str | None = None) -> None: + """Registers another :class:`Command` with this group. If the name + is not provided, the name of the command is used. + """ + name = name or cmd.name + if name is None: + raise TypeError("Command has no name.") + _check_nested_chain(self, name, cmd, register=True) + self.commands[name] = cmd + + @t.overload + def command(self, __func: t.Callable[..., t.Any]) -> Command: ... + + @t.overload + def command( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Callable[[t.Callable[..., t.Any]], Command]: ... + + def command( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Callable[[t.Callable[..., t.Any]], Command] | Command: + """A shortcut decorator for declaring and attaching a command to + the group. This takes the same arguments as :func:`command` and + immediately registers the created command with this group by + calling :meth:`add_command`. + + To customize the command class used, set the + :attr:`command_class` attribute. + + .. versionchanged:: 8.1 + This decorator can be applied without parentheses. + + .. versionchanged:: 8.0 + Added the :attr:`command_class` attribute. + """ + from .decorators import command + + func: t.Callable[..., t.Any] | None = None + + if args and callable(args[0]): + assert len(args) == 1 and not kwargs, ( + "Use 'command(**kwargs)(callable)' to provide arguments." + ) + (func,) = args + args = () + + if self.command_class and kwargs.get("cls") is None: + kwargs["cls"] = self.command_class + + def decorator(f: t.Callable[..., t.Any]) -> Command: + cmd: Command = command(*args, **kwargs)(f) + self.add_command(cmd) + return cmd + + if func is not None: + return decorator(func) + + return decorator + + @t.overload + def group(self, __func: t.Callable[..., t.Any]) -> Group: ... + + @t.overload + def group( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Callable[[t.Callable[..., t.Any]], Group]: ... + + def group( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Callable[[t.Callable[..., t.Any]], Group] | Group: + """A shortcut decorator for declaring and attaching a group to + the group. This takes the same arguments as :func:`group` and + immediately registers the created group with this group by + calling :meth:`add_command`. + + To customize the group class used, set the :attr:`group_class` + attribute. + + .. versionchanged:: 8.1 + This decorator can be applied without parentheses. + + .. versionchanged:: 8.0 + Added the :attr:`group_class` attribute. + """ + from .decorators import group + + func: t.Callable[..., t.Any] | None = None + + if args and callable(args[0]): + assert len(args) == 1 and not kwargs, ( + "Use 'group(**kwargs)(callable)' to provide arguments." + ) + (func,) = args + args = () + + if self.group_class is not None and kwargs.get("cls") is None: + if self.group_class is type: + kwargs["cls"] = type(self) + else: + kwargs["cls"] = self.group_class + + def decorator(f: t.Callable[..., t.Any]) -> Group: + cmd: Group = group(*args, **kwargs)(f) + self.add_command(cmd) + return cmd + + if func is not None: + return decorator(func) + + return decorator + + def result_callback(self, replace: bool = False) -> t.Callable[[F], F]: + """Adds a result callback to the command. By default if a + result callback is already registered this will chain them but + this can be disabled with the `replace` parameter. The result + callback is invoked with the return value of the subcommand + (or the list of return values from all subcommands if chaining + is enabled) as well as the parameters as they would be passed + to the main callback. + + Example:: + + @click.group() + @click.option('-i', '--input', default=23) + def cli(input): + return 42 + + @cli.result_callback() + def process_result(result, input): + return result + input + + :param replace: if set to `True` an already existing result + callback will be removed. + + .. versionchanged:: 8.0 + Renamed from ``resultcallback``. + + .. versionadded:: 3.0 + """ + + def decorator(f: F) -> F: + old_callback = self._result_callback + + if old_callback is None or replace: + self._result_callback = f + return f + + def function(value: t.Any, /, *args: t.Any, **kwargs: t.Any) -> t.Any: + inner = old_callback(value, *args, **kwargs) + return f(inner, *args, **kwargs) + + self._result_callback = rv = update_wrapper(t.cast(F, function), f) + return rv # type: ignore[return-value] + + return decorator + + def get_command(self, ctx: Context, cmd_name: str) -> Command | None: + """Given a context and a command name, this returns a :class:`Command` + object if it exists or returns ``None``. + """ + return self.commands.get(cmd_name) + + def list_commands(self, ctx: Context) -> list[str]: + """Returns a list of subcommand names in the order they should appear.""" + return sorted(self.commands) + + def collect_usage_pieces(self, ctx: Context) -> list[str]: + rv = super().collect_usage_pieces(ctx) + rv.append(self.subcommand_metavar) + return rv + + def format_options(self, ctx: Context, formatter: HelpFormatter) -> None: + super().format_options(ctx, formatter) + self.format_commands(ctx, formatter) + + def format_commands(self, ctx: Context, formatter: HelpFormatter) -> None: + """Extra format methods for multi methods that adds all the commands + after the options. + """ + commands = [] + for subcommand in self.list_commands(ctx): + cmd = self.get_command(ctx, subcommand) + # What is this, the tool lied about a command. Ignore it + if cmd is None: + continue + if cmd.hidden: + continue + + commands.append((subcommand, cmd)) + + # allow for 3 times the default spacing + if len(commands): + limit = formatter.width - 6 - max(len(cmd[0]) for cmd in commands) + + rows = [] + for subcommand, cmd in commands: + help = cmd.get_short_help_str(limit) + rows.append((subcommand, help)) + + if rows: + with formatter.section(_("Commands")): + formatter.write_dl(rows) + + def parse_args(self, ctx: Context, args: list[str]) -> list[str]: + if not args and self.no_args_is_help and not ctx.resilient_parsing: + raise NoArgsIsHelpError(ctx) + + rest = super().parse_args(ctx, args) + + if self.chain: + ctx._protected_args = rest + ctx.args = [] + elif rest: + ctx._protected_args, ctx.args = rest[:1], rest[1:] + + return ctx.args + + def invoke(self, ctx: Context) -> t.Any: + def _process_result(value: t.Any) -> t.Any: + if self._result_callback is not None: + value = ctx.invoke(self._result_callback, value, **ctx.params) + return value + + if not ctx._protected_args: + if self.invoke_without_command: + # No subcommand was invoked, so the result callback is + # invoked with the group return value for regular + # groups, or an empty list for chained groups. + with ctx: + rv = super().invoke(ctx) + return _process_result([] if self.chain else rv) + ctx.fail(_("Missing command.")) + + # Fetch args back out + args = [*ctx._protected_args, *ctx.args] + ctx.args = [] + ctx._protected_args = [] + + # If we're not in chain mode, we only allow the invocation of a + # single command but we also inform the current context about the + # name of the command to invoke. + if not self.chain: + # Make sure the context is entered so we do not clean up + # resources until the result processor has worked. + with ctx: + cmd_name, cmd, args = self.resolve_command(ctx, args) + assert cmd is not None + ctx.invoked_subcommand = cmd_name + super().invoke(ctx) + sub_ctx = cmd.make_context(cmd_name, args, parent=ctx) + with sub_ctx: + return _process_result(sub_ctx.command.invoke(sub_ctx)) + + # In chain mode we create the contexts step by step, but after the + # base command has been invoked. Because at that point we do not + # know the subcommands yet, the invoked subcommand attribute is + # set to ``*`` to inform the command that subcommands are executed + # but nothing else. + with ctx: + ctx.invoked_subcommand = "*" if args else None + super().invoke(ctx) + + # Otherwise we make every single context and invoke them in a + # chain. In that case the return value to the result processor + # is the list of all invoked subcommand's results. + contexts = [] + while args: + cmd_name, cmd, args = self.resolve_command(ctx, args) + assert cmd is not None + sub_ctx = cmd.make_context( + cmd_name, + args, + parent=ctx, + allow_extra_args=True, + allow_interspersed_args=False, + ) + contexts.append(sub_ctx) + args, sub_ctx.args = sub_ctx.args, [] + + rv = [] + for sub_ctx in contexts: + with sub_ctx: + rv.append(sub_ctx.command.invoke(sub_ctx)) + return _process_result(rv) + + def resolve_command( + self, ctx: Context, args: list[str] + ) -> tuple[str | None, Command | None, list[str]]: + cmd_name = make_str(args[0]) + original_cmd_name = cmd_name + + # Get the command + cmd = self.get_command(ctx, cmd_name) + + # If we can't find the command but there is a normalization + # function available, we try with that one. + if cmd is None and ctx.token_normalize_func is not None: + cmd_name = ctx.token_normalize_func(cmd_name) + cmd = self.get_command(ctx, cmd_name) + + # If we don't find the command we want to show an error message + # to the user that it was not provided. However, there is + # something else we should do: if the first argument looks like + # an option we want to kick off parsing again for arguments to + # resolve things like --help which now should go to the main + # place. + if cmd is None and not ctx.resilient_parsing: + if _split_opt(cmd_name)[0]: + self.parse_args(ctx, args) + ctx.fail(_("No such command {name!r}.").format(name=original_cmd_name)) + return cmd_name if cmd else None, cmd, args[1:] + + def shell_complete(self, ctx: Context, incomplete: str) -> list[CompletionItem]: + """Return a list of completions for the incomplete value. Looks + at the names of options, subcommands, and chained + multi-commands. + + :param ctx: Invocation context for this command. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + results = [ + CompletionItem(name, help=command.get_short_help_str()) + for name, command in _complete_visible_commands(ctx, incomplete) + ] + results.extend(super().shell_complete(ctx, incomplete)) + return results + + +class _MultiCommand(Group, metaclass=_FakeSubclassCheck): + """ + .. deprecated:: 8.2 + Will be removed in Click 9.0. Use ``Group`` instead. + """ + + +class CommandCollection(Group): + """A :class:`Group` that looks up subcommands on other groups. If a command + is not found on this group, each registered source is checked in order. + Parameters on a source are not added to this group, and a source's callback + is not invoked when invoking its commands. In other words, this "flattens" + commands in many groups into this one group. + + :param name: The name of the group command. + :param sources: A list of :class:`Group` objects to look up commands from. + :param kwargs: Other arguments passed to :class:`Group`. + + .. versionchanged:: 8.2 + This is a subclass of ``Group``. Commands are looked up first on this + group, then each of its sources. + """ + + def __init__( + self, + name: str | None = None, + sources: list[Group] | None = None, + **kwargs: t.Any, + ) -> None: + super().__init__(name, **kwargs) + #: The list of registered groups. + self.sources: list[Group] = sources or [] + + def add_source(self, group: Group) -> None: + """Add a group as a source of commands.""" + self.sources.append(group) + + def get_command(self, ctx: Context, cmd_name: str) -> Command | None: + rv = super().get_command(ctx, cmd_name) + + if rv is not None: + return rv + + for source in self.sources: + rv = source.get_command(ctx, cmd_name) + + if rv is not None: + if self.chain: + _check_nested_chain(self, cmd_name, rv) + + return rv + + return None + + def list_commands(self, ctx: Context) -> list[str]: + rv: set[str] = set(super().list_commands(ctx)) + + for source in self.sources: + rv.update(source.list_commands(ctx)) + + return sorted(rv) + + +def _check_iter(value: t.Any) -> cabc.Iterator[t.Any]: + """Check if the value is iterable but not a string. Raises a type + error, or return an iterator over the value. + """ + if isinstance(value, str): + raise TypeError + + return iter(value) + + +class Parameter: + r"""A parameter to a command comes in two versions: they are either + :class:`Option`\s or :class:`Argument`\s. Other subclasses are currently + not supported by design as some of the internals for parsing are + intentionally not finalized. + + Some settings are supported by both options and arguments. + + :param param_decls: the parameter declarations for this option or + argument. This is a list of flags or argument + names. + :param type: the type that should be used. Either a :class:`ParamType` + or a Python type. The latter is converted into the former + automatically if supported. + :param required: controls if this is optional or not. + :param default: the default value if omitted. This can also be a callable, + in which case it's invoked when the default is needed + without any arguments. + :param callback: A function to further process or validate the value + after type conversion. It is called as ``f(ctx, param, value)`` + and must return the value. It is called for all sources, + including prompts. + :param nargs: the number of arguments to match. If not ``1`` the return + value is a tuple instead of single value. The default for + nargs is ``1`` (except if the type is a tuple, then it's + the arity of the tuple). If ``nargs=-1``, all remaining + parameters are collected. + :param metavar: how the value is represented in the help page. + :param expose_value: if this is `True` then the value is passed onwards + to the command callback and stored on the context, + otherwise it's skipped. + :param is_eager: eager values are processed before non eager ones. This + should not be set for arguments or it will inverse the + order of processing. + :param envvar: a string or list of strings that are environment variables + that should be checked. + :param shell_complete: A function that returns custom shell + completions. Used instead of the param's type completion if + given. Takes ``ctx, param, incomplete`` and must return a list + of :class:`~click.shell_completion.CompletionItem` or a list of + strings. + :param deprecated: If ``True`` or non-empty string, issues a message + indicating that the argument is deprecated and highlights + its deprecation in --help. The message can be customized + by using a string as the value. A deprecated parameter + cannot be required, a ValueError will be raised otherwise. + + .. versionchanged:: 8.2.0 + Introduction of ``deprecated``. + + .. versionchanged:: 8.2 + Adding duplicate parameter names to a :class:`~click.core.Command` will + result in a ``UserWarning`` being shown. + + .. versionchanged:: 8.2 + Adding duplicate parameter names to a :class:`~click.core.Command` will + result in a ``UserWarning`` being shown. + + .. versionchanged:: 8.0 + ``process_value`` validates required parameters and bounded + ``nargs``, and invokes the parameter callback before returning + the value. This allows the callback to validate prompts. + ``full_process_value`` is removed. + + .. versionchanged:: 8.0 + ``autocompletion`` is renamed to ``shell_complete`` and has new + semantics described above. The old name is deprecated and will + be removed in 8.1, until then it will be wrapped to match the + new requirements. + + .. versionchanged:: 8.0 + For ``multiple=True, nargs>1``, the default must be a list of + tuples. + + .. versionchanged:: 8.0 + Setting a default is no longer required for ``nargs>1``, it will + default to ``None``. ``multiple=True`` or ``nargs=-1`` will + default to ``()``. + + .. versionchanged:: 7.1 + Empty environment variables are ignored rather than taking the + empty string value. This makes it possible for scripts to clear + variables if they can't unset them. + + .. versionchanged:: 2.0 + Changed signature for parameter callback to also be passed the + parameter. The old callback format will still work, but it will + raise a warning to give you a chance to migrate the code easier. + """ + + param_type_name = "parameter" + + def __init__( + self, + param_decls: cabc.Sequence[str] | None = None, + type: types.ParamType | t.Any | None = None, + required: bool = False, + default: t.Any | t.Callable[[], t.Any] | None = None, + callback: t.Callable[[Context, Parameter, t.Any], t.Any] | None = None, + nargs: int | None = None, + multiple: bool = False, + metavar: str | None = None, + expose_value: bool = True, + is_eager: bool = False, + envvar: str | cabc.Sequence[str] | None = None, + shell_complete: t.Callable[ + [Context, Parameter, str], list[CompletionItem] | list[str] + ] + | None = None, + deprecated: bool | str = False, + ) -> None: + self.name: str | None + self.opts: list[str] + self.secondary_opts: list[str] + self.name, self.opts, self.secondary_opts = self._parse_decls( + param_decls or (), expose_value + ) + self.type: types.ParamType = types.convert_type(type, default) + + # Default nargs to what the type tells us if we have that + # information available. + if nargs is None: + if self.type.is_composite: + nargs = self.type.arity + else: + nargs = 1 + + self.required = required + self.callback = callback + self.nargs = nargs + self.multiple = multiple + self.expose_value = expose_value + self.default = default + self.is_eager = is_eager + self.metavar = metavar + self.envvar = envvar + self._custom_shell_complete = shell_complete + self.deprecated = deprecated + + if __debug__: + if self.type.is_composite and nargs != self.type.arity: + raise ValueError( + f"'nargs' must be {self.type.arity} (or None) for" + f" type {self.type!r}, but it was {nargs}." + ) + + # Skip no default or callable default. + check_default = default if not callable(default) else None + + if check_default is not None: + if multiple: + try: + # Only check the first value against nargs. + check_default = next(_check_iter(check_default), None) + except TypeError: + raise ValueError( + "'default' must be a list when 'multiple' is true." + ) from None + + # Can be None for multiple with empty default. + if nargs != 1 and check_default is not None: + try: + _check_iter(check_default) + except TypeError: + if multiple: + message = ( + "'default' must be a list of lists when 'multiple' is" + " true and 'nargs' != 1." + ) + else: + message = "'default' must be a list when 'nargs' != 1." + + raise ValueError(message) from None + + if nargs > 1 and len(check_default) != nargs: + subject = "item length" if multiple else "length" + raise ValueError( + f"'default' {subject} must match nargs={nargs}." + ) + + if required and deprecated: + raise ValueError( + f"The {self.param_type_name} '{self.human_readable_name}' " + "is deprecated and still required. A deprecated " + f"{self.param_type_name} cannot be required." + ) + + def to_info_dict(self) -> dict[str, t.Any]: + """Gather information that could be useful for a tool generating + user-facing documentation. + + Use :meth:`click.Context.to_info_dict` to traverse the entire + CLI structure. + + .. versionadded:: 8.0 + """ + return { + "name": self.name, + "param_type_name": self.param_type_name, + "opts": self.opts, + "secondary_opts": self.secondary_opts, + "type": self.type.to_info_dict(), + "required": self.required, + "nargs": self.nargs, + "multiple": self.multiple, + "default": self.default, + "envvar": self.envvar, + } + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} {self.name}>" + + def _parse_decls( + self, decls: cabc.Sequence[str], expose_value: bool + ) -> tuple[str | None, list[str], list[str]]: + raise NotImplementedError() + + @property + def human_readable_name(self) -> str: + """Returns the human readable name of this parameter. This is the + same as the name for options, but the metavar for arguments. + """ + return self.name # type: ignore + + def make_metavar(self, ctx: Context) -> str: + if self.metavar is not None: + return self.metavar + + metavar = self.type.get_metavar(param=self, ctx=ctx) + + if metavar is None: + metavar = self.type.name.upper() + + if self.nargs != 1: + metavar += "..." + + return metavar + + @t.overload + def get_default( + self, ctx: Context, call: t.Literal[True] = True + ) -> t.Any | None: ... + + @t.overload + def get_default( + self, ctx: Context, call: bool = ... + ) -> t.Any | t.Callable[[], t.Any] | None: ... + + def get_default( + self, ctx: Context, call: bool = True + ) -> t.Any | t.Callable[[], t.Any] | None: + """Get the default for the parameter. Tries + :meth:`Context.lookup_default` first, then the local default. + + :param ctx: Current context. + :param call: If the default is a callable, call it. Disable to + return the callable instead. + + .. versionchanged:: 8.0.2 + Type casting is no longer performed when getting a default. + + .. versionchanged:: 8.0.1 + Type casting can fail in resilient parsing mode. Invalid + defaults will not prevent showing help text. + + .. versionchanged:: 8.0 + Looks at ``ctx.default_map`` first. + + .. versionchanged:: 8.0 + Added the ``call`` parameter. + """ + value = ctx.lookup_default(self.name, call=False) # type: ignore + + if value is None: + value = self.default + + if call and callable(value): + value = value() + + return value + + def add_to_parser(self, parser: _OptionParser, ctx: Context) -> None: + raise NotImplementedError() + + def consume_value( + self, ctx: Context, opts: cabc.Mapping[str, t.Any] + ) -> tuple[t.Any, ParameterSource]: + value = opts.get(self.name) # type: ignore + source = ParameterSource.COMMANDLINE + + if value is None: + value = self.value_from_envvar(ctx) + source = ParameterSource.ENVIRONMENT + + if value is None: + value = ctx.lookup_default(self.name) # type: ignore + source = ParameterSource.DEFAULT_MAP + + if value is None: + value = self.get_default(ctx) + source = ParameterSource.DEFAULT + + return value, source + + def type_cast_value(self, ctx: Context, value: t.Any) -> t.Any: + """Convert and validate a value against the option's + :attr:`type`, :attr:`multiple`, and :attr:`nargs`. + """ + if value is None: + return () if self.multiple or self.nargs == -1 else None + + def check_iter(value: t.Any) -> cabc.Iterator[t.Any]: + try: + return _check_iter(value) + except TypeError: + # This should only happen when passing in args manually, + # the parser should construct an iterable when parsing + # the command line. + raise BadParameter( + _("Value must be an iterable."), ctx=ctx, param=self + ) from None + + if self.nargs == 1 or self.type.is_composite: + + def convert(value: t.Any) -> t.Any: + return self.type(value, param=self, ctx=ctx) + + elif self.nargs == -1: + + def convert(value: t.Any) -> t.Any: # tuple[t.Any, ...] + return tuple(self.type(x, self, ctx) for x in check_iter(value)) + + else: # nargs > 1 + + def convert(value: t.Any) -> t.Any: # tuple[t.Any, ...] + value = tuple(check_iter(value)) + + if len(value) != self.nargs: + raise BadParameter( + ngettext( + "Takes {nargs} values but 1 was given.", + "Takes {nargs} values but {len} were given.", + len(value), + ).format(nargs=self.nargs, len=len(value)), + ctx=ctx, + param=self, + ) + + return tuple(self.type(x, self, ctx) for x in value) + + if self.multiple: + return tuple(convert(x) for x in check_iter(value)) + + return convert(value) + + def value_is_missing(self, value: t.Any) -> bool: + if value is None: + return True + + if (self.nargs != 1 or self.multiple) and value == (): + return True + + return False + + def process_value(self, ctx: Context, value: t.Any) -> t.Any: + value = self.type_cast_value(ctx, value) + + if self.required and self.value_is_missing(value): + raise MissingParameter(ctx=ctx, param=self) + + if self.callback is not None: + value = self.callback(ctx, self, value) + + return value + + def resolve_envvar_value(self, ctx: Context) -> str | None: + if self.envvar is None: + return None + + if isinstance(self.envvar, str): + rv = os.environ.get(self.envvar) + + if rv: + return rv + else: + for envvar in self.envvar: + rv = os.environ.get(envvar) + + if rv: + return rv + + return None + + def value_from_envvar(self, ctx: Context) -> t.Any | None: + rv: t.Any | None = self.resolve_envvar_value(ctx) + + if rv is not None and self.nargs != 1: + rv = self.type.split_envvar_value(rv) + + return rv + + def handle_parse_result( + self, ctx: Context, opts: cabc.Mapping[str, t.Any], args: list[str] + ) -> tuple[t.Any, list[str]]: + with augment_usage_errors(ctx, param=self): + value, source = self.consume_value(ctx, opts) + + if ( + self.deprecated + and value is not None + and source + not in ( + ParameterSource.DEFAULT, + ParameterSource.DEFAULT_MAP, + ) + ): + extra_message = ( + f" {self.deprecated}" if isinstance(self.deprecated, str) else "" + ) + message = _( + "DeprecationWarning: The {param_type} {name!r} is deprecated." + "{extra_message}" + ).format( + param_type=self.param_type_name, + name=self.human_readable_name, + extra_message=extra_message, + ) + echo(style(message, fg="red"), err=True) + + ctx.set_parameter_source(self.name, source) # type: ignore + + try: + value = self.process_value(ctx, value) + except Exception: + if not ctx.resilient_parsing: + raise + + value = None + + if self.expose_value: + ctx.params[self.name] = value # type: ignore + + return value, args + + def get_help_record(self, ctx: Context) -> tuple[str, str] | None: + pass + + def get_usage_pieces(self, ctx: Context) -> list[str]: + return [] + + def get_error_hint(self, ctx: Context) -> str: + """Get a stringified version of the param for use in error messages to + indicate which param caused the error. + """ + hint_list = self.opts or [self.human_readable_name] + return " / ".join(f"'{x}'" for x in hint_list) + + def shell_complete(self, ctx: Context, incomplete: str) -> list[CompletionItem]: + """Return a list of completions for the incomplete value. If a + ``shell_complete`` function was given during init, it is used. + Otherwise, the :attr:`type` + :meth:`~click.types.ParamType.shell_complete` function is used. + + :param ctx: Invocation context for this command. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + if self._custom_shell_complete is not None: + results = self._custom_shell_complete(ctx, self, incomplete) + + if results and isinstance(results[0], str): + from click.shell_completion import CompletionItem + + results = [CompletionItem(c) for c in results] + + return t.cast("list[CompletionItem]", results) + + return self.type.shell_complete(ctx, self, incomplete) + + +class Option(Parameter): + """Options are usually optional values on the command line and + have some extra features that arguments don't have. + + All other parameters are passed onwards to the parameter constructor. + + :param show_default: Show the default value for this option in its + help text. Values are not shown by default, unless + :attr:`Context.show_default` is ``True``. If this value is a + string, it shows that string in parentheses instead of the + actual value. This is particularly useful for dynamic options. + For single option boolean flags, the default remains hidden if + its value is ``False``. + :param show_envvar: Controls if an environment variable should be + shown on the help page and error messages. + Normally, environment variables are not shown. + :param prompt: If set to ``True`` or a non empty string then the + user will be prompted for input. If set to ``True`` the prompt + will be the option name capitalized. A deprecated option cannot be + prompted. + :param confirmation_prompt: Prompt a second time to confirm the + value if it was prompted for. Can be set to a string instead of + ``True`` to customize the message. + :param prompt_required: If set to ``False``, the user will be + prompted for input only when the option was specified as a flag + without a value. + :param hide_input: If this is ``True`` then the input on the prompt + will be hidden from the user. This is useful for password input. + :param is_flag: forces this option to act as a flag. The default is + auto detection. + :param flag_value: which value should be used for this flag if it's + enabled. This is set to a boolean automatically if + the option string contains a slash to mark two options. + :param multiple: if this is set to `True` then the argument is accepted + multiple times and recorded. This is similar to ``nargs`` + in how it works but supports arbitrary number of + arguments. + :param count: this flag makes an option increment an integer. + :param allow_from_autoenv: if this is enabled then the value of this + parameter will be pulled from an environment + variable in case a prefix is defined on the + context. + :param help: the help string. + :param hidden: hide this option from help outputs. + :param attrs: Other command arguments described in :class:`Parameter`. + + .. versionchanged:: 8.2 + ``envvar`` used with ``flag_value`` will always use the ``flag_value``, + previously it would use the value of the environment variable. + + .. versionchanged:: 8.1 + Help text indentation is cleaned here instead of only in the + ``@option`` decorator. + + .. versionchanged:: 8.1 + The ``show_default`` parameter overrides + ``Context.show_default``. + + .. versionchanged:: 8.1 + The default of a single option boolean flag is not shown if the + default value is ``False``. + + .. versionchanged:: 8.0.1 + ``type`` is detected from ``flag_value`` if given. + """ + + param_type_name = "option" + + def __init__( + self, + param_decls: cabc.Sequence[str] | None = None, + show_default: bool | str | None = None, + prompt: bool | str = False, + confirmation_prompt: bool | str = False, + prompt_required: bool = True, + hide_input: bool = False, + is_flag: bool | None = None, + flag_value: t.Any | None = None, + multiple: bool = False, + count: bool = False, + allow_from_autoenv: bool = True, + type: types.ParamType | t.Any | None = None, + help: str | None = None, + hidden: bool = False, + show_choices: bool = True, + show_envvar: bool = False, + deprecated: bool | str = False, + **attrs: t.Any, + ) -> None: + if help: + help = inspect.cleandoc(help) + + default_is_missing = "default" not in attrs + super().__init__( + param_decls, type=type, multiple=multiple, deprecated=deprecated, **attrs + ) + + if prompt is True: + if self.name is None: + raise TypeError("'name' is required with 'prompt=True'.") + + prompt_text: str | None = self.name.replace("_", " ").capitalize() + elif prompt is False: + prompt_text = None + else: + prompt_text = prompt + + if deprecated: + deprecated_message = ( + f"(DEPRECATED: {deprecated})" + if isinstance(deprecated, str) + else "(DEPRECATED)" + ) + help = help + deprecated_message if help is not None else deprecated_message + + self.prompt = prompt_text + self.confirmation_prompt = confirmation_prompt + self.prompt_required = prompt_required + self.hide_input = hide_input + self.hidden = hidden + + # If prompt is enabled but not required, then the option can be + # used as a flag to indicate using prompt or flag_value. + self._flag_needs_value = self.prompt is not None and not self.prompt_required + + if is_flag is None: + if flag_value is not None: + # Implicitly a flag because flag_value was set. + is_flag = True + elif self._flag_needs_value: + # Not a flag, but when used as a flag it shows a prompt. + is_flag = False + else: + # Implicitly a flag because flag options were given. + is_flag = bool(self.secondary_opts) + elif is_flag is False and not self._flag_needs_value: + # Not a flag, and prompt is not enabled, can be used as a + # flag if flag_value is set. + self._flag_needs_value = flag_value is not None + + self.default: t.Any | t.Callable[[], t.Any] + + if is_flag and default_is_missing and not self.required: + if multiple: + self.default = () + else: + self.default = False + + if is_flag and flag_value is None: + flag_value = not self.default + + self.type: types.ParamType + if is_flag and type is None: + # Re-guess the type from the flag value instead of the + # default. + self.type = types.convert_type(None, flag_value) + + self.is_flag: bool = is_flag + self.is_bool_flag: bool = is_flag and isinstance(self.type, types.BoolParamType) + self.flag_value: t.Any = flag_value + + # Counting + self.count = count + if count: + if type is None: + self.type = types.IntRange(min=0) + if default_is_missing: + self.default = 0 + + self.allow_from_autoenv = allow_from_autoenv + self.help = help + self.show_default = show_default + self.show_choices = show_choices + self.show_envvar = show_envvar + + if __debug__: + if deprecated and prompt: + raise ValueError("`deprecated` options cannot use `prompt`.") + + if self.nargs == -1: + raise TypeError("nargs=-1 is not supported for options.") + + if self.prompt and self.is_flag and not self.is_bool_flag: + raise TypeError("'prompt' is not valid for non-boolean flag.") + + if not self.is_bool_flag and self.secondary_opts: + raise TypeError("Secondary flag is not valid for non-boolean flag.") + + if self.is_bool_flag and self.hide_input and self.prompt is not None: + raise TypeError( + "'prompt' with 'hide_input' is not valid for boolean flag." + ) + + if self.count: + if self.multiple: + raise TypeError("'count' is not valid with 'multiple'.") + + if self.is_flag: + raise TypeError("'count' is not valid with 'is_flag'.") + + def to_info_dict(self) -> dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict.update( + help=self.help, + prompt=self.prompt, + is_flag=self.is_flag, + flag_value=self.flag_value, + count=self.count, + hidden=self.hidden, + ) + return info_dict + + def get_error_hint(self, ctx: Context) -> str: + result = super().get_error_hint(ctx) + if self.show_envvar: + result += f" (env var: '{self.envvar}')" + return result + + def _parse_decls( + self, decls: cabc.Sequence[str], expose_value: bool + ) -> tuple[str | None, list[str], list[str]]: + opts = [] + secondary_opts = [] + name = None + possible_names = [] + + for decl in decls: + if decl.isidentifier(): + if name is not None: + raise TypeError(f"Name '{name}' defined twice") + name = decl + else: + split_char = ";" if decl[:1] == "/" else "/" + if split_char in decl: + first, second = decl.split(split_char, 1) + first = first.rstrip() + if first: + possible_names.append(_split_opt(first)) + opts.append(first) + second = second.lstrip() + if second: + secondary_opts.append(second.lstrip()) + if first == second: + raise ValueError( + f"Boolean option {decl!r} cannot use the" + " same flag for true/false." + ) + else: + possible_names.append(_split_opt(decl)) + opts.append(decl) + + if name is None and possible_names: + possible_names.sort(key=lambda x: -len(x[0])) # group long options first + name = possible_names[0][1].replace("-", "_").lower() + if not name.isidentifier(): + name = None + + if name is None: + if not expose_value: + return None, opts, secondary_opts + raise TypeError( + f"Could not determine name for option with declarations {decls!r}" + ) + + if not opts and not secondary_opts: + raise TypeError( + f"No options defined but a name was passed ({name})." + " Did you mean to declare an argument instead? Did" + f" you mean to pass '--{name}'?" + ) + + return name, opts, secondary_opts + + def add_to_parser(self, parser: _OptionParser, ctx: Context) -> None: + if self.multiple: + action = "append" + elif self.count: + action = "count" + else: + action = "store" + + if self.is_flag: + action = f"{action}_const" + + if self.is_bool_flag and self.secondary_opts: + parser.add_option( + obj=self, opts=self.opts, dest=self.name, action=action, const=True + ) + parser.add_option( + obj=self, + opts=self.secondary_opts, + dest=self.name, + action=action, + const=False, + ) + else: + parser.add_option( + obj=self, + opts=self.opts, + dest=self.name, + action=action, + const=self.flag_value, + ) + else: + parser.add_option( + obj=self, + opts=self.opts, + dest=self.name, + action=action, + nargs=self.nargs, + ) + + def get_help_record(self, ctx: Context) -> tuple[str, str] | None: + if self.hidden: + return None + + any_prefix_is_slash = False + + def _write_opts(opts: cabc.Sequence[str]) -> str: + nonlocal any_prefix_is_slash + + rv, any_slashes = join_options(opts) + + if any_slashes: + any_prefix_is_slash = True + + if not self.is_flag and not self.count: + rv += f" {self.make_metavar(ctx=ctx)}" + + return rv + + rv = [_write_opts(self.opts)] + + if self.secondary_opts: + rv.append(_write_opts(self.secondary_opts)) + + help = self.help or "" + + extra = self.get_help_extra(ctx) + extra_items = [] + if "envvars" in extra: + extra_items.append( + _("env var: {var}").format(var=", ".join(extra["envvars"])) + ) + if "default" in extra: + extra_items.append(_("default: {default}").format(default=extra["default"])) + if "range" in extra: + extra_items.append(extra["range"]) + if "required" in extra: + extra_items.append(_(extra["required"])) + + if extra_items: + extra_str = "; ".join(extra_items) + help = f"{help} [{extra_str}]" if help else f"[{extra_str}]" + + return ("; " if any_prefix_is_slash else " / ").join(rv), help + + def get_help_extra(self, ctx: Context) -> types.OptionHelpExtra: + extra: types.OptionHelpExtra = {} + + if self.show_envvar: + envvar = self.envvar + + if envvar is None: + if ( + self.allow_from_autoenv + and ctx.auto_envvar_prefix is not None + and self.name is not None + ): + envvar = f"{ctx.auto_envvar_prefix}_{self.name.upper()}" + + if envvar is not None: + if isinstance(envvar, str): + extra["envvars"] = (envvar,) + else: + extra["envvars"] = tuple(str(d) for d in envvar) + + # Temporarily enable resilient parsing to avoid type casting + # failing for the default. Might be possible to extend this to + # help formatting in general. + resilient = ctx.resilient_parsing + ctx.resilient_parsing = True + + try: + default_value = self.get_default(ctx, call=False) + finally: + ctx.resilient_parsing = resilient + + show_default = False + show_default_is_str = False + + if self.show_default is not None: + if isinstance(self.show_default, str): + show_default_is_str = show_default = True + else: + show_default = self.show_default + elif ctx.show_default is not None: + show_default = ctx.show_default + + if show_default_is_str or (show_default and (default_value is not None)): + if show_default_is_str: + default_string = f"({self.show_default})" + elif isinstance(default_value, (list, tuple)): + default_string = ", ".join(str(d) for d in default_value) + elif inspect.isfunction(default_value): + default_string = _("(dynamic)") + elif self.is_bool_flag and self.secondary_opts: + # For boolean flags that have distinct True/False opts, + # use the opt without prefix instead of the value. + default_string = _split_opt( + (self.opts if default_value else self.secondary_opts)[0] + )[1] + elif self.is_bool_flag and not self.secondary_opts and not default_value: + default_string = "" + elif default_value == "": + default_string = '""' + else: + default_string = str(default_value) + + if default_string: + extra["default"] = default_string + + if ( + isinstance(self.type, types._NumberRangeBase) + # skip count with default range type + and not (self.count and self.type.min == 0 and self.type.max is None) + ): + range_str = self.type._describe_range() + + if range_str: + extra["range"] = range_str + + if self.required: + extra["required"] = "required" + + return extra + + @t.overload + def get_default( + self, ctx: Context, call: t.Literal[True] = True + ) -> t.Any | None: ... + + @t.overload + def get_default( + self, ctx: Context, call: bool = ... + ) -> t.Any | t.Callable[[], t.Any] | None: ... + + def get_default( + self, ctx: Context, call: bool = True + ) -> t.Any | t.Callable[[], t.Any] | None: + # If we're a non boolean flag our default is more complex because + # we need to look at all flags in the same group to figure out + # if we're the default one in which case we return the flag + # value as default. + if self.is_flag and not self.is_bool_flag: + for param in ctx.command.params: + if param.name == self.name and param.default: + return t.cast(Option, param).flag_value + + return None + + return super().get_default(ctx, call=call) + + def prompt_for_value(self, ctx: Context) -> t.Any: + """This is an alternative flow that can be activated in the full + value processing if a value does not exist. It will prompt the + user until a valid value exists and then returns the processed + value as result. + """ + assert self.prompt is not None + + # Calculate the default before prompting anything to be stable. + default = self.get_default(ctx) + + # If this is a prompt for a flag we need to handle this + # differently. + if self.is_bool_flag: + return confirm(self.prompt, default) + + # If show_default is set to True/False, provide this to `prompt` as well. For + # non-bool values of `show_default`, we use `prompt`'s default behavior + prompt_kwargs: t.Any = {} + if isinstance(self.show_default, bool): + prompt_kwargs["show_default"] = self.show_default + + return prompt( + self.prompt, + default=default, + type=self.type, + hide_input=self.hide_input, + show_choices=self.show_choices, + confirmation_prompt=self.confirmation_prompt, + value_proc=lambda x: self.process_value(ctx, x), + **prompt_kwargs, + ) + + def resolve_envvar_value(self, ctx: Context) -> str | None: + rv = super().resolve_envvar_value(ctx) + + if rv is not None: + if self.is_flag and self.flag_value: + return str(self.flag_value) + return rv + + if ( + self.allow_from_autoenv + and ctx.auto_envvar_prefix is not None + and self.name is not None + ): + envvar = f"{ctx.auto_envvar_prefix}_{self.name.upper()}" + rv = os.environ.get(envvar) + + if rv: + return rv + + return None + + def value_from_envvar(self, ctx: Context) -> t.Any | None: + rv: t.Any | None = self.resolve_envvar_value(ctx) + + if rv is None: + return None + + value_depth = (self.nargs != 1) + bool(self.multiple) + + if value_depth > 0: + rv = self.type.split_envvar_value(rv) + + if self.multiple and self.nargs != 1: + rv = batch(rv, self.nargs) + + return rv + + def consume_value( + self, ctx: Context, opts: cabc.Mapping[str, Parameter] + ) -> tuple[t.Any, ParameterSource]: + value, source = super().consume_value(ctx, opts) + + # The parser will emit a sentinel value if the option can be + # given as a flag without a value. This is different from None + # to distinguish from the flag not being given at all. + if value is _flag_needs_value: + if self.prompt is not None and not ctx.resilient_parsing: + value = self.prompt_for_value(ctx) + source = ParameterSource.PROMPT + else: + value = self.flag_value + source = ParameterSource.COMMANDLINE + + elif ( + self.multiple + and value is not None + and any(v is _flag_needs_value for v in value) + ): + value = [self.flag_value if v is _flag_needs_value else v for v in value] + source = ParameterSource.COMMANDLINE + + # The value wasn't set, or used the param's default, prompt if + # prompting is enabled. + elif ( + source in {None, ParameterSource.DEFAULT} + and self.prompt is not None + and (self.required or self.prompt_required) + and not ctx.resilient_parsing + ): + value = self.prompt_for_value(ctx) + source = ParameterSource.PROMPT + + return value, source + + +class Argument(Parameter): + """Arguments are positional parameters to a command. They generally + provide fewer features than options but can have infinite ``nargs`` + and are required by default. + + All parameters are passed onwards to the constructor of :class:`Parameter`. + """ + + param_type_name = "argument" + + def __init__( + self, + param_decls: cabc.Sequence[str], + required: bool | None = None, + **attrs: t.Any, + ) -> None: + if required is None: + if attrs.get("default") is not None: + required = False + else: + required = attrs.get("nargs", 1) > 0 + + if "multiple" in attrs: + raise TypeError("__init__() got an unexpected keyword argument 'multiple'.") + + super().__init__(param_decls, required=required, **attrs) + + if __debug__: + if self.default is not None and self.nargs == -1: + raise TypeError("'default' is not supported for nargs=-1.") + + @property + def human_readable_name(self) -> str: + if self.metavar is not None: + return self.metavar + return self.name.upper() # type: ignore + + def make_metavar(self, ctx: Context) -> str: + if self.metavar is not None: + return self.metavar + var = self.type.get_metavar(param=self, ctx=ctx) + if not var: + var = self.name.upper() # type: ignore + if self.deprecated: + var += "!" + if not self.required: + var = f"[{var}]" + if self.nargs != 1: + var += "..." + return var + + def _parse_decls( + self, decls: cabc.Sequence[str], expose_value: bool + ) -> tuple[str | None, list[str], list[str]]: + if not decls: + if not expose_value: + return None, [], [] + raise TypeError("Argument is marked as exposed, but does not have a name.") + if len(decls) == 1: + name = arg = decls[0] + name = name.replace("-", "_").lower() + else: + raise TypeError( + "Arguments take exactly one parameter declaration, got" + f" {len(decls)}: {decls}." + ) + return name, [arg], [] + + def get_usage_pieces(self, ctx: Context) -> list[str]: + return [self.make_metavar(ctx)] + + def get_error_hint(self, ctx: Context) -> str: + return f"'{self.make_metavar(ctx)}'" + + def add_to_parser(self, parser: _OptionParser, ctx: Context) -> None: + parser.add_argument(dest=self.name, nargs=self.nargs, obj=self) + + +def __getattr__(name: str) -> object: + import warnings + + if name == "BaseCommand": + warnings.warn( + "'BaseCommand' is deprecated and will be removed in Click 9.0. Use" + " 'Command' instead.", + DeprecationWarning, + stacklevel=2, + ) + return _BaseCommand + + if name == "MultiCommand": + warnings.warn( + "'MultiCommand' is deprecated and will be removed in Click 9.0. Use" + " 'Group' instead.", + DeprecationWarning, + stacklevel=2, + ) + return _MultiCommand + + raise AttributeError(name) diff --git a/venv/lib/python3.12/site-packages/click/decorators.py b/venv/lib/python3.12/site-packages/click/decorators.py new file mode 100644 index 00000000..21f4c342 --- /dev/null +++ b/venv/lib/python3.12/site-packages/click/decorators.py @@ -0,0 +1,551 @@ +from __future__ import annotations + +import inspect +import typing as t +from functools import update_wrapper +from gettext import gettext as _ + +from .core import Argument +from .core import Command +from .core import Context +from .core import Group +from .core import Option +from .core import Parameter +from .globals import get_current_context +from .utils import echo + +if t.TYPE_CHECKING: + import typing_extensions as te + + P = te.ParamSpec("P") + +R = t.TypeVar("R") +T = t.TypeVar("T") +_AnyCallable = t.Callable[..., t.Any] +FC = t.TypeVar("FC", bound="_AnyCallable | Command") + + +def pass_context(f: t.Callable[te.Concatenate[Context, P], R]) -> t.Callable[P, R]: + """Marks a callback as wanting to receive the current context + object as first argument. + """ + + def new_func(*args: P.args, **kwargs: P.kwargs) -> R: + return f(get_current_context(), *args, **kwargs) + + return update_wrapper(new_func, f) + + +def pass_obj(f: t.Callable[te.Concatenate[T, P], R]) -> t.Callable[P, R]: + """Similar to :func:`pass_context`, but only pass the object on the + context onwards (:attr:`Context.obj`). This is useful if that object + represents the state of a nested system. + """ + + def new_func(*args: P.args, **kwargs: P.kwargs) -> R: + return f(get_current_context().obj, *args, **kwargs) + + return update_wrapper(new_func, f) + + +def make_pass_decorator( + object_type: type[T], ensure: bool = False +) -> t.Callable[[t.Callable[te.Concatenate[T, P], R]], t.Callable[P, R]]: + """Given an object type this creates a decorator that will work + similar to :func:`pass_obj` but instead of passing the object of the + current context, it will find the innermost context of type + :func:`object_type`. + + This generates a decorator that works roughly like this:: + + from functools import update_wrapper + + def decorator(f): + @pass_context + def new_func(ctx, *args, **kwargs): + obj = ctx.find_object(object_type) + return ctx.invoke(f, obj, *args, **kwargs) + return update_wrapper(new_func, f) + return decorator + + :param object_type: the type of the object to pass. + :param ensure: if set to `True`, a new object will be created and + remembered on the context if it's not there yet. + """ + + def decorator(f: t.Callable[te.Concatenate[T, P], R]) -> t.Callable[P, R]: + def new_func(*args: P.args, **kwargs: P.kwargs) -> R: + ctx = get_current_context() + + obj: T | None + if ensure: + obj = ctx.ensure_object(object_type) + else: + obj = ctx.find_object(object_type) + + if obj is None: + raise RuntimeError( + "Managed to invoke callback without a context" + f" object of type {object_type.__name__!r}" + " existing." + ) + + return ctx.invoke(f, obj, *args, **kwargs) + + return update_wrapper(new_func, f) + + return decorator + + +def pass_meta_key( + key: str, *, doc_description: str | None = None +) -> t.Callable[[t.Callable[te.Concatenate[T, P], R]], t.Callable[P, R]]: + """Create a decorator that passes a key from + :attr:`click.Context.meta` as the first argument to the decorated + function. + + :param key: Key in ``Context.meta`` to pass. + :param doc_description: Description of the object being passed, + inserted into the decorator's docstring. Defaults to "the 'key' + key from Context.meta". + + .. versionadded:: 8.0 + """ + + def decorator(f: t.Callable[te.Concatenate[T, P], R]) -> t.Callable[P, R]: + def new_func(*args: P.args, **kwargs: P.kwargs) -> R: + ctx = get_current_context() + obj = ctx.meta[key] + return ctx.invoke(f, obj, *args, **kwargs) + + return update_wrapper(new_func, f) + + if doc_description is None: + doc_description = f"the {key!r} key from :attr:`click.Context.meta`" + + decorator.__doc__ = ( + f"Decorator that passes {doc_description} as the first argument" + " to the decorated function." + ) + return decorator + + +CmdType = t.TypeVar("CmdType", bound=Command) + + +# variant: no call, directly as decorator for a function. +@t.overload +def command(name: _AnyCallable) -> Command: ... + + +# variant: with positional name and with positional or keyword cls argument: +# @command(namearg, CommandCls, ...) or @command(namearg, cls=CommandCls, ...) +@t.overload +def command( + name: str | None, + cls: type[CmdType], + **attrs: t.Any, +) -> t.Callable[[_AnyCallable], CmdType]: ... + + +# variant: name omitted, cls _must_ be a keyword argument, @command(cls=CommandCls, ...) +@t.overload +def command( + name: None = None, + *, + cls: type[CmdType], + **attrs: t.Any, +) -> t.Callable[[_AnyCallable], CmdType]: ... + + +# variant: with optional string name, no cls argument provided. +@t.overload +def command( + name: str | None = ..., cls: None = None, **attrs: t.Any +) -> t.Callable[[_AnyCallable], Command]: ... + + +def command( + name: str | _AnyCallable | None = None, + cls: type[CmdType] | None = None, + **attrs: t.Any, +) -> Command | t.Callable[[_AnyCallable], Command | CmdType]: + r"""Creates a new :class:`Command` and uses the decorated function as + callback. This will also automatically attach all decorated + :func:`option`\s and :func:`argument`\s as parameters to the command. + + The name of the command defaults to the name of the function, converted to + lowercase, with underscores ``_`` replaced by dashes ``-``, and the suffixes + ``_command``, ``_cmd``, ``_group``, and ``_grp`` are removed. For example, + ``init_data_command`` becomes ``init-data``. + + All keyword arguments are forwarded to the underlying command class. + For the ``params`` argument, any decorated params are appended to + the end of the list. + + Once decorated the function turns into a :class:`Command` instance + that can be invoked as a command line utility or be attached to a + command :class:`Group`. + + :param name: The name of the command. Defaults to modifying the function's + name as described above. + :param cls: The command class to create. Defaults to :class:`Command`. + + .. versionchanged:: 8.2 + The suffixes ``_command``, ``_cmd``, ``_group``, and ``_grp`` are + removed when generating the name. + + .. versionchanged:: 8.1 + This decorator can be applied without parentheses. + + .. versionchanged:: 8.1 + The ``params`` argument can be used. Decorated params are + appended to the end of the list. + """ + + func: t.Callable[[_AnyCallable], t.Any] | None = None + + if callable(name): + func = name + name = None + assert cls is None, "Use 'command(cls=cls)(callable)' to specify a class." + assert not attrs, "Use 'command(**kwargs)(callable)' to provide arguments." + + if cls is None: + cls = t.cast("type[CmdType]", Command) + + def decorator(f: _AnyCallable) -> CmdType: + if isinstance(f, Command): + raise TypeError("Attempted to convert a callback into a command twice.") + + attr_params = attrs.pop("params", None) + params = attr_params if attr_params is not None else [] + + try: + decorator_params = f.__click_params__ # type: ignore + except AttributeError: + pass + else: + del f.__click_params__ # type: ignore + params.extend(reversed(decorator_params)) + + if attrs.get("help") is None: + attrs["help"] = f.__doc__ + + if t.TYPE_CHECKING: + assert cls is not None + assert not callable(name) + + if name is not None: + cmd_name = name + else: + cmd_name = f.__name__.lower().replace("_", "-") + cmd_left, sep, suffix = cmd_name.rpartition("-") + + if sep and suffix in {"command", "cmd", "group", "grp"}: + cmd_name = cmd_left + + cmd = cls(name=cmd_name, callback=f, params=params, **attrs) + cmd.__doc__ = f.__doc__ + return cmd + + if func is not None: + return decorator(func) + + return decorator + + +GrpType = t.TypeVar("GrpType", bound=Group) + + +# variant: no call, directly as decorator for a function. +@t.overload +def group(name: _AnyCallable) -> Group: ... + + +# variant: with positional name and with positional or keyword cls argument: +# @group(namearg, GroupCls, ...) or @group(namearg, cls=GroupCls, ...) +@t.overload +def group( + name: str | None, + cls: type[GrpType], + **attrs: t.Any, +) -> t.Callable[[_AnyCallable], GrpType]: ... + + +# variant: name omitted, cls _must_ be a keyword argument, @group(cmd=GroupCls, ...) +@t.overload +def group( + name: None = None, + *, + cls: type[GrpType], + **attrs: t.Any, +) -> t.Callable[[_AnyCallable], GrpType]: ... + + +# variant: with optional string name, no cls argument provided. +@t.overload +def group( + name: str | None = ..., cls: None = None, **attrs: t.Any +) -> t.Callable[[_AnyCallable], Group]: ... + + +def group( + name: str | _AnyCallable | None = None, + cls: type[GrpType] | None = None, + **attrs: t.Any, +) -> Group | t.Callable[[_AnyCallable], Group | GrpType]: + """Creates a new :class:`Group` with a function as callback. This + works otherwise the same as :func:`command` just that the `cls` + parameter is set to :class:`Group`. + + .. versionchanged:: 8.1 + This decorator can be applied without parentheses. + """ + if cls is None: + cls = t.cast("type[GrpType]", Group) + + if callable(name): + return command(cls=cls, **attrs)(name) + + return command(name, cls, **attrs) + + +def _param_memo(f: t.Callable[..., t.Any], param: Parameter) -> None: + if isinstance(f, Command): + f.params.append(param) + else: + if not hasattr(f, "__click_params__"): + f.__click_params__ = [] # type: ignore + + f.__click_params__.append(param) # type: ignore + + +def argument( + *param_decls: str, cls: type[Argument] | None = None, **attrs: t.Any +) -> t.Callable[[FC], FC]: + """Attaches an argument to the command. All positional arguments are + passed as parameter declarations to :class:`Argument`; all keyword + arguments are forwarded unchanged (except ``cls``). + This is equivalent to creating an :class:`Argument` instance manually + and attaching it to the :attr:`Command.params` list. + + For the default argument class, refer to :class:`Argument` and + :class:`Parameter` for descriptions of parameters. + + :param cls: the argument class to instantiate. This defaults to + :class:`Argument`. + :param param_decls: Passed as positional arguments to the constructor of + ``cls``. + :param attrs: Passed as keyword arguments to the constructor of ``cls``. + """ + if cls is None: + cls = Argument + + def decorator(f: FC) -> FC: + _param_memo(f, cls(param_decls, **attrs)) + return f + + return decorator + + +def option( + *param_decls: str, cls: type[Option] | None = None, **attrs: t.Any +) -> t.Callable[[FC], FC]: + """Attaches an option to the command. All positional arguments are + passed as parameter declarations to :class:`Option`; all keyword + arguments are forwarded unchanged (except ``cls``). + This is equivalent to creating an :class:`Option` instance manually + and attaching it to the :attr:`Command.params` list. + + For the default option class, refer to :class:`Option` and + :class:`Parameter` for descriptions of parameters. + + :param cls: the option class to instantiate. This defaults to + :class:`Option`. + :param param_decls: Passed as positional arguments to the constructor of + ``cls``. + :param attrs: Passed as keyword arguments to the constructor of ``cls``. + """ + if cls is None: + cls = Option + + def decorator(f: FC) -> FC: + _param_memo(f, cls(param_decls, **attrs)) + return f + + return decorator + + +def confirmation_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: + """Add a ``--yes`` option which shows a prompt before continuing if + not passed. If the prompt is declined, the program will exit. + + :param param_decls: One or more option names. Defaults to the single + value ``"--yes"``. + :param kwargs: Extra arguments are passed to :func:`option`. + """ + + def callback(ctx: Context, param: Parameter, value: bool) -> None: + if not value: + ctx.abort() + + if not param_decls: + param_decls = ("--yes",) + + kwargs.setdefault("is_flag", True) + kwargs.setdefault("callback", callback) + kwargs.setdefault("expose_value", False) + kwargs.setdefault("prompt", "Do you want to continue?") + kwargs.setdefault("help", "Confirm the action without prompting.") + return option(*param_decls, **kwargs) + + +def password_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: + """Add a ``--password`` option which prompts for a password, hiding + input and asking to enter the value again for confirmation. + + :param param_decls: One or more option names. Defaults to the single + value ``"--password"``. + :param kwargs: Extra arguments are passed to :func:`option`. + """ + if not param_decls: + param_decls = ("--password",) + + kwargs.setdefault("prompt", True) + kwargs.setdefault("confirmation_prompt", True) + kwargs.setdefault("hide_input", True) + return option(*param_decls, **kwargs) + + +def version_option( + version: str | None = None, + *param_decls: str, + package_name: str | None = None, + prog_name: str | None = None, + message: str | None = None, + **kwargs: t.Any, +) -> t.Callable[[FC], FC]: + """Add a ``--version`` option which immediately prints the version + number and exits the program. + + If ``version`` is not provided, Click will try to detect it using + :func:`importlib.metadata.version` to get the version for the + ``package_name``. + + If ``package_name`` is not provided, Click will try to detect it by + inspecting the stack frames. This will be used to detect the + version, so it must match the name of the installed package. + + :param version: The version number to show. If not provided, Click + will try to detect it. + :param param_decls: One or more option names. Defaults to the single + value ``"--version"``. + :param package_name: The package name to detect the version from. If + not provided, Click will try to detect it. + :param prog_name: The name of the CLI to show in the message. If not + provided, it will be detected from the command. + :param message: The message to show. The values ``%(prog)s``, + ``%(package)s``, and ``%(version)s`` are available. Defaults to + ``"%(prog)s, version %(version)s"``. + :param kwargs: Extra arguments are passed to :func:`option`. + :raise RuntimeError: ``version`` could not be detected. + + .. versionchanged:: 8.0 + Add the ``package_name`` parameter, and the ``%(package)s`` + value for messages. + + .. versionchanged:: 8.0 + Use :mod:`importlib.metadata` instead of ``pkg_resources``. The + version is detected based on the package name, not the entry + point name. The Python package name must match the installed + package name, or be passed with ``package_name=``. + """ + if message is None: + message = _("%(prog)s, version %(version)s") + + if version is None and package_name is None: + frame = inspect.currentframe() + f_back = frame.f_back if frame is not None else None + f_globals = f_back.f_globals if f_back is not None else None + # break reference cycle + # https://docs.python.org/3/library/inspect.html#the-interpreter-stack + del frame + + if f_globals is not None: + package_name = f_globals.get("__name__") + + if package_name == "__main__": + package_name = f_globals.get("__package__") + + if package_name: + package_name = package_name.partition(".")[0] + + def callback(ctx: Context, param: Parameter, value: bool) -> None: + if not value or ctx.resilient_parsing: + return + + nonlocal prog_name + nonlocal version + + if prog_name is None: + prog_name = ctx.find_root().info_name + + if version is None and package_name is not None: + import importlib.metadata + + try: + version = importlib.metadata.version(package_name) + except importlib.metadata.PackageNotFoundError: + raise RuntimeError( + f"{package_name!r} is not installed. Try passing" + " 'package_name' instead." + ) from None + + if version is None: + raise RuntimeError( + f"Could not determine the version for {package_name!r} automatically." + ) + + echo( + message % {"prog": prog_name, "package": package_name, "version": version}, + color=ctx.color, + ) + ctx.exit() + + if not param_decls: + param_decls = ("--version",) + + kwargs.setdefault("is_flag", True) + kwargs.setdefault("expose_value", False) + kwargs.setdefault("is_eager", True) + kwargs.setdefault("help", _("Show the version and exit.")) + kwargs["callback"] = callback + return option(*param_decls, **kwargs) + + +def help_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: + """Pre-configured ``--help`` option which immediately prints the help page + and exits the program. + + :param param_decls: One or more option names. Defaults to the single + value ``"--help"``. + :param kwargs: Extra arguments are passed to :func:`option`. + """ + + def show_help(ctx: Context, param: Parameter, value: bool) -> None: + """Callback that print the help page on ```` and exits.""" + if value and not ctx.resilient_parsing: + echo(ctx.get_help(), color=ctx.color) + ctx.exit() + + if not param_decls: + param_decls = ("--help",) + + kwargs.setdefault("is_flag", True) + kwargs.setdefault("expose_value", False) + kwargs.setdefault("is_eager", True) + kwargs.setdefault("help", _("Show this message and exit.")) + kwargs.setdefault("callback", show_help) + + return option(*param_decls, **kwargs) diff --git a/venv/lib/python3.12/site-packages/click/exceptions.py b/venv/lib/python3.12/site-packages/click/exceptions.py new file mode 100644 index 00000000..f141a832 --- /dev/null +++ b/venv/lib/python3.12/site-packages/click/exceptions.py @@ -0,0 +1,308 @@ +from __future__ import annotations + +import collections.abc as cabc +import typing as t +from gettext import gettext as _ +from gettext import ngettext + +from ._compat import get_text_stderr +from .globals import resolve_color_default +from .utils import echo +from .utils import format_filename + +if t.TYPE_CHECKING: + from .core import Command + from .core import Context + from .core import Parameter + + +def _join_param_hints(param_hint: cabc.Sequence[str] | str | None) -> str | None: + if param_hint is not None and not isinstance(param_hint, str): + return " / ".join(repr(x) for x in param_hint) + + return param_hint + + +class ClickException(Exception): + """An exception that Click can handle and show to the user.""" + + #: The exit code for this exception. + exit_code = 1 + + def __init__(self, message: str) -> None: + super().__init__(message) + # The context will be removed by the time we print the message, so cache + # the color settings here to be used later on (in `show`) + self.show_color: bool | None = resolve_color_default() + self.message = message + + def format_message(self) -> str: + return self.message + + def __str__(self) -> str: + return self.message + + def show(self, file: t.IO[t.Any] | None = None) -> None: + if file is None: + file = get_text_stderr() + + echo( + _("Error: {message}").format(message=self.format_message()), + file=file, + color=self.show_color, + ) + + +class UsageError(ClickException): + """An internal exception that signals a usage error. This typically + aborts any further handling. + + :param message: the error message to display. + :param ctx: optionally the context that caused this error. Click will + fill in the context automatically in some situations. + """ + + exit_code = 2 + + def __init__(self, message: str, ctx: Context | None = None) -> None: + super().__init__(message) + self.ctx = ctx + self.cmd: Command | None = self.ctx.command if self.ctx else None + + def show(self, file: t.IO[t.Any] | None = None) -> None: + if file is None: + file = get_text_stderr() + color = None + hint = "" + if ( + self.ctx is not None + and self.ctx.command.get_help_option(self.ctx) is not None + ): + hint = _("Try '{command} {option}' for help.").format( + command=self.ctx.command_path, option=self.ctx.help_option_names[0] + ) + hint = f"{hint}\n" + if self.ctx is not None: + color = self.ctx.color + echo(f"{self.ctx.get_usage()}\n{hint}", file=file, color=color) + echo( + _("Error: {message}").format(message=self.format_message()), + file=file, + color=color, + ) + + +class BadParameter(UsageError): + """An exception that formats out a standardized error message for a + bad parameter. This is useful when thrown from a callback or type as + Click will attach contextual information to it (for instance, which + parameter it is). + + .. versionadded:: 2.0 + + :param param: the parameter object that caused this error. This can + be left out, and Click will attach this info itself + if possible. + :param param_hint: a string that shows up as parameter name. This + can be used as alternative to `param` in cases + where custom validation should happen. If it is + a string it's used as such, if it's a list then + each item is quoted and separated. + """ + + def __init__( + self, + message: str, + ctx: Context | None = None, + param: Parameter | None = None, + param_hint: str | None = None, + ) -> None: + super().__init__(message, ctx) + self.param = param + self.param_hint = param_hint + + def format_message(self) -> str: + if self.param_hint is not None: + param_hint = self.param_hint + elif self.param is not None: + param_hint = self.param.get_error_hint(self.ctx) # type: ignore + else: + return _("Invalid value: {message}").format(message=self.message) + + return _("Invalid value for {param_hint}: {message}").format( + param_hint=_join_param_hints(param_hint), message=self.message + ) + + +class MissingParameter(BadParameter): + """Raised if click required an option or argument but it was not + provided when invoking the script. + + .. versionadded:: 4.0 + + :param param_type: a string that indicates the type of the parameter. + The default is to inherit the parameter type from + the given `param`. Valid values are ``'parameter'``, + ``'option'`` or ``'argument'``. + """ + + def __init__( + self, + message: str | None = None, + ctx: Context | None = None, + param: Parameter | None = None, + param_hint: str | None = None, + param_type: str | None = None, + ) -> None: + super().__init__(message or "", ctx, param, param_hint) + self.param_type = param_type + + def format_message(self) -> str: + if self.param_hint is not None: + param_hint: str | None = self.param_hint + elif self.param is not None: + param_hint = self.param.get_error_hint(self.ctx) # type: ignore + else: + param_hint = None + + param_hint = _join_param_hints(param_hint) + param_hint = f" {param_hint}" if param_hint else "" + + param_type = self.param_type + if param_type is None and self.param is not None: + param_type = self.param.param_type_name + + msg = self.message + if self.param is not None: + msg_extra = self.param.type.get_missing_message( + param=self.param, ctx=self.ctx + ) + if msg_extra: + if msg: + msg += f". {msg_extra}" + else: + msg = msg_extra + + msg = f" {msg}" if msg else "" + + # Translate param_type for known types. + if param_type == "argument": + missing = _("Missing argument") + elif param_type == "option": + missing = _("Missing option") + elif param_type == "parameter": + missing = _("Missing parameter") + else: + missing = _("Missing {param_type}").format(param_type=param_type) + + return f"{missing}{param_hint}.{msg}" + + def __str__(self) -> str: + if not self.message: + param_name = self.param.name if self.param else None + return _("Missing parameter: {param_name}").format(param_name=param_name) + else: + return self.message + + +class NoSuchOption(UsageError): + """Raised if click attempted to handle an option that does not + exist. + + .. versionadded:: 4.0 + """ + + def __init__( + self, + option_name: str, + message: str | None = None, + possibilities: cabc.Sequence[str] | None = None, + ctx: Context | None = None, + ) -> None: + if message is None: + message = _("No such option: {name}").format(name=option_name) + + super().__init__(message, ctx) + self.option_name = option_name + self.possibilities = possibilities + + def format_message(self) -> str: + if not self.possibilities: + return self.message + + possibility_str = ", ".join(sorted(self.possibilities)) + suggest = ngettext( + "Did you mean {possibility}?", + "(Possible options: {possibilities})", + len(self.possibilities), + ).format(possibility=possibility_str, possibilities=possibility_str) + return f"{self.message} {suggest}" + + +class BadOptionUsage(UsageError): + """Raised if an option is generally supplied but the use of the option + was incorrect. This is for instance raised if the number of arguments + for an option is not correct. + + .. versionadded:: 4.0 + + :param option_name: the name of the option being used incorrectly. + """ + + def __init__( + self, option_name: str, message: str, ctx: Context | None = None + ) -> None: + super().__init__(message, ctx) + self.option_name = option_name + + +class BadArgumentUsage(UsageError): + """Raised if an argument is generally supplied but the use of the argument + was incorrect. This is for instance raised if the number of values + for an argument is not correct. + + .. versionadded:: 6.0 + """ + + +class NoArgsIsHelpError(UsageError): + def __init__(self, ctx: Context) -> None: + self.ctx: Context + super().__init__(ctx.get_help(), ctx=ctx) + + def show(self, file: t.IO[t.Any] | None = None) -> None: + echo(self.format_message(), file=file, err=True, color=self.ctx.color) + + +class FileError(ClickException): + """Raised if a file cannot be opened.""" + + def __init__(self, filename: str, hint: str | None = None) -> None: + if hint is None: + hint = _("unknown error") + + super().__init__(hint) + self.ui_filename: str = format_filename(filename) + self.filename = filename + + def format_message(self) -> str: + return _("Could not open file {filename!r}: {message}").format( + filename=self.ui_filename, message=self.message + ) + + +class Abort(RuntimeError): + """An internal signalling exception that signals Click to abort.""" + + +class Exit(RuntimeError): + """An exception that indicates that the application should exit with some + status code. + + :param code: the status code to exit with. + """ + + __slots__ = ("exit_code",) + + def __init__(self, code: int = 0) -> None: + self.exit_code: int = code diff --git a/venv/lib/python3.12/site-packages/click/formatting.py b/venv/lib/python3.12/site-packages/click/formatting.py new file mode 100644 index 00000000..9891f880 --- /dev/null +++ b/venv/lib/python3.12/site-packages/click/formatting.py @@ -0,0 +1,301 @@ +from __future__ import annotations + +import collections.abc as cabc +from contextlib import contextmanager +from gettext import gettext as _ + +from ._compat import term_len +from .parser import _split_opt + +# Can force a width. This is used by the test system +FORCED_WIDTH: int | None = None + + +def measure_table(rows: cabc.Iterable[tuple[str, str]]) -> tuple[int, ...]: + widths: dict[int, int] = {} + + for row in rows: + for idx, col in enumerate(row): + widths[idx] = max(widths.get(idx, 0), term_len(col)) + + return tuple(y for x, y in sorted(widths.items())) + + +def iter_rows( + rows: cabc.Iterable[tuple[str, str]], col_count: int +) -> cabc.Iterator[tuple[str, ...]]: + for row in rows: + yield row + ("",) * (col_count - len(row)) + + +def wrap_text( + text: str, + width: int = 78, + initial_indent: str = "", + subsequent_indent: str = "", + preserve_paragraphs: bool = False, +) -> str: + """A helper function that intelligently wraps text. By default, it + assumes that it operates on a single paragraph of text but if the + `preserve_paragraphs` parameter is provided it will intelligently + handle paragraphs (defined by two empty lines). + + If paragraphs are handled, a paragraph can be prefixed with an empty + line containing the ``\\b`` character (``\\x08``) to indicate that + no rewrapping should happen in that block. + + :param text: the text that should be rewrapped. + :param width: the maximum width for the text. + :param initial_indent: the initial indent that should be placed on the + first line as a string. + :param subsequent_indent: the indent string that should be placed on + each consecutive line. + :param preserve_paragraphs: if this flag is set then the wrapping will + intelligently handle paragraphs. + """ + from ._textwrap import TextWrapper + + text = text.expandtabs() + wrapper = TextWrapper( + width, + initial_indent=initial_indent, + subsequent_indent=subsequent_indent, + replace_whitespace=False, + ) + if not preserve_paragraphs: + return wrapper.fill(text) + + p: list[tuple[int, bool, str]] = [] + buf: list[str] = [] + indent = None + + def _flush_par() -> None: + if not buf: + return + if buf[0].strip() == "\b": + p.append((indent or 0, True, "\n".join(buf[1:]))) + else: + p.append((indent or 0, False, " ".join(buf))) + del buf[:] + + for line in text.splitlines(): + if not line: + _flush_par() + indent = None + else: + if indent is None: + orig_len = term_len(line) + line = line.lstrip() + indent = orig_len - term_len(line) + buf.append(line) + _flush_par() + + rv = [] + for indent, raw, text in p: + with wrapper.extra_indent(" " * indent): + if raw: + rv.append(wrapper.indent_only(text)) + else: + rv.append(wrapper.fill(text)) + + return "\n\n".join(rv) + + +class HelpFormatter: + """This class helps with formatting text-based help pages. It's + usually just needed for very special internal cases, but it's also + exposed so that developers can write their own fancy outputs. + + At present, it always writes into memory. + + :param indent_increment: the additional increment for each level. + :param width: the width for the text. This defaults to the terminal + width clamped to a maximum of 78. + """ + + def __init__( + self, + indent_increment: int = 2, + width: int | None = None, + max_width: int | None = None, + ) -> None: + import shutil + + self.indent_increment = indent_increment + if max_width is None: + max_width = 80 + if width is None: + width = FORCED_WIDTH + if width is None: + width = max(min(shutil.get_terminal_size().columns, max_width) - 2, 50) + self.width = width + self.current_indent: int = 0 + self.buffer: list[str] = [] + + def write(self, string: str) -> None: + """Writes a unicode string into the internal buffer.""" + self.buffer.append(string) + + def indent(self) -> None: + """Increases the indentation.""" + self.current_indent += self.indent_increment + + def dedent(self) -> None: + """Decreases the indentation.""" + self.current_indent -= self.indent_increment + + def write_usage(self, prog: str, args: str = "", prefix: str | None = None) -> None: + """Writes a usage line into the buffer. + + :param prog: the program name. + :param args: whitespace separated list of arguments. + :param prefix: The prefix for the first line. Defaults to + ``"Usage: "``. + """ + if prefix is None: + prefix = f"{_('Usage:')} " + + usage_prefix = f"{prefix:>{self.current_indent}}{prog} " + text_width = self.width - self.current_indent + + if text_width >= (term_len(usage_prefix) + 20): + # The arguments will fit to the right of the prefix. + indent = " " * term_len(usage_prefix) + self.write( + wrap_text( + args, + text_width, + initial_indent=usage_prefix, + subsequent_indent=indent, + ) + ) + else: + # The prefix is too long, put the arguments on the next line. + self.write(usage_prefix) + self.write("\n") + indent = " " * (max(self.current_indent, term_len(prefix)) + 4) + self.write( + wrap_text( + args, text_width, initial_indent=indent, subsequent_indent=indent + ) + ) + + self.write("\n") + + def write_heading(self, heading: str) -> None: + """Writes a heading into the buffer.""" + self.write(f"{'':>{self.current_indent}}{heading}:\n") + + def write_paragraph(self) -> None: + """Writes a paragraph into the buffer.""" + if self.buffer: + self.write("\n") + + def write_text(self, text: str) -> None: + """Writes re-indented text into the buffer. This rewraps and + preserves paragraphs. + """ + indent = " " * self.current_indent + self.write( + wrap_text( + text, + self.width, + initial_indent=indent, + subsequent_indent=indent, + preserve_paragraphs=True, + ) + ) + self.write("\n") + + def write_dl( + self, + rows: cabc.Sequence[tuple[str, str]], + col_max: int = 30, + col_spacing: int = 2, + ) -> None: + """Writes a definition list into the buffer. This is how options + and commands are usually formatted. + + :param rows: a list of two item tuples for the terms and values. + :param col_max: the maximum width of the first column. + :param col_spacing: the number of spaces between the first and + second column. + """ + rows = list(rows) + widths = measure_table(rows) + if len(widths) != 2: + raise TypeError("Expected two columns for definition list") + + first_col = min(widths[0], col_max) + col_spacing + + for first, second in iter_rows(rows, len(widths)): + self.write(f"{'':>{self.current_indent}}{first}") + if not second: + self.write("\n") + continue + if term_len(first) <= first_col - col_spacing: + self.write(" " * (first_col - term_len(first))) + else: + self.write("\n") + self.write(" " * (first_col + self.current_indent)) + + text_width = max(self.width - first_col - 2, 10) + wrapped_text = wrap_text(second, text_width, preserve_paragraphs=True) + lines = wrapped_text.splitlines() + + if lines: + self.write(f"{lines[0]}\n") + + for line in lines[1:]: + self.write(f"{'':>{first_col + self.current_indent}}{line}\n") + else: + self.write("\n") + + @contextmanager + def section(self, name: str) -> cabc.Iterator[None]: + """Helpful context manager that writes a paragraph, a heading, + and the indents. + + :param name: the section name that is written as heading. + """ + self.write_paragraph() + self.write_heading(name) + self.indent() + try: + yield + finally: + self.dedent() + + @contextmanager + def indentation(self) -> cabc.Iterator[None]: + """A context manager that increases the indentation.""" + self.indent() + try: + yield + finally: + self.dedent() + + def getvalue(self) -> str: + """Returns the buffer contents.""" + return "".join(self.buffer) + + +def join_options(options: cabc.Sequence[str]) -> tuple[str, bool]: + """Given a list of option strings this joins them in the most appropriate + way and returns them in the form ``(formatted_string, + any_prefix_is_slash)`` where the second item in the tuple is a flag that + indicates if any of the option prefixes was a slash. + """ + rv = [] + any_prefix_is_slash = False + + for opt in options: + prefix = _split_opt(opt)[0] + + if prefix == "/": + any_prefix_is_slash = True + + rv.append((len(prefix), opt)) + + rv.sort(key=lambda x: x[0]) + return ", ".join(x[1] for x in rv), any_prefix_is_slash diff --git a/venv/lib/python3.12/site-packages/click/globals.py b/venv/lib/python3.12/site-packages/click/globals.py new file mode 100644 index 00000000..a2f91723 --- /dev/null +++ b/venv/lib/python3.12/site-packages/click/globals.py @@ -0,0 +1,67 @@ +from __future__ import annotations + +import typing as t +from threading import local + +if t.TYPE_CHECKING: + from .core import Context + +_local = local() + + +@t.overload +def get_current_context(silent: t.Literal[False] = False) -> Context: ... + + +@t.overload +def get_current_context(silent: bool = ...) -> Context | None: ... + + +def get_current_context(silent: bool = False) -> Context | None: + """Returns the current click context. This can be used as a way to + access the current context object from anywhere. This is a more implicit + alternative to the :func:`pass_context` decorator. This function is + primarily useful for helpers such as :func:`echo` which might be + interested in changing its behavior based on the current context. + + To push the current context, :meth:`Context.scope` can be used. + + .. versionadded:: 5.0 + + :param silent: if set to `True` the return value is `None` if no context + is available. The default behavior is to raise a + :exc:`RuntimeError`. + """ + try: + return t.cast("Context", _local.stack[-1]) + except (AttributeError, IndexError) as e: + if not silent: + raise RuntimeError("There is no active click context.") from e + + return None + + +def push_context(ctx: Context) -> None: + """Pushes a new context to the current stack.""" + _local.__dict__.setdefault("stack", []).append(ctx) + + +def pop_context() -> None: + """Removes the top level from the stack.""" + _local.stack.pop() + + +def resolve_color_default(color: bool | None = None) -> bool | None: + """Internal helper to get the default value of the color flag. If a + value is passed it's returned unchanged, otherwise it's looked up from + the current context. + """ + if color is not None: + return color + + ctx = get_current_context(silent=True) + + if ctx is not None: + return ctx.color + + return None diff --git a/venv/lib/python3.12/site-packages/click/parser.py b/venv/lib/python3.12/site-packages/click/parser.py new file mode 100644 index 00000000..a8b7d263 --- /dev/null +++ b/venv/lib/python3.12/site-packages/click/parser.py @@ -0,0 +1,532 @@ +""" +This module started out as largely a copy paste from the stdlib's +optparse module with the features removed that we do not need from +optparse because we implement them in Click on a higher level (for +instance type handling, help formatting and a lot more). + +The plan is to remove more and more from here over time. + +The reason this is a different module and not optparse from the stdlib +is that there are differences in 2.x and 3.x about the error messages +generated and optparse in the stdlib uses gettext for no good reason +and might cause us issues. + +Click uses parts of optparse written by Gregory P. Ward and maintained +by the Python Software Foundation. This is limited to code in parser.py. + +Copyright 2001-2006 Gregory P. Ward. All rights reserved. +Copyright 2002-2006 Python Software Foundation. All rights reserved. +""" + +# This code uses parts of optparse written by Gregory P. Ward and +# maintained by the Python Software Foundation. +# Copyright 2001-2006 Gregory P. Ward +# Copyright 2002-2006 Python Software Foundation +from __future__ import annotations + +import collections.abc as cabc +import typing as t +from collections import deque +from gettext import gettext as _ +from gettext import ngettext + +from .exceptions import BadArgumentUsage +from .exceptions import BadOptionUsage +from .exceptions import NoSuchOption +from .exceptions import UsageError + +if t.TYPE_CHECKING: + from .core import Argument as CoreArgument + from .core import Context + from .core import Option as CoreOption + from .core import Parameter as CoreParameter + +V = t.TypeVar("V") + +# Sentinel value that indicates an option was passed as a flag without a +# value but is not a flag option. Option.consume_value uses this to +# prompt or use the flag_value. +_flag_needs_value = object() + + +def _unpack_args( + args: cabc.Sequence[str], nargs_spec: cabc.Sequence[int] +) -> tuple[cabc.Sequence[str | cabc.Sequence[str | None] | None], list[str]]: + """Given an iterable of arguments and an iterable of nargs specifications, + it returns a tuple with all the unpacked arguments at the first index + and all remaining arguments as the second. + + The nargs specification is the number of arguments that should be consumed + or `-1` to indicate that this position should eat up all the remainders. + + Missing items are filled with `None`. + """ + args = deque(args) + nargs_spec = deque(nargs_spec) + rv: list[str | tuple[str | None, ...] | None] = [] + spos: int | None = None + + def _fetch(c: deque[V]) -> V | None: + try: + if spos is None: + return c.popleft() + else: + return c.pop() + except IndexError: + return None + + while nargs_spec: + nargs = _fetch(nargs_spec) + + if nargs is None: + continue + + if nargs == 1: + rv.append(_fetch(args)) + elif nargs > 1: + x = [_fetch(args) for _ in range(nargs)] + + # If we're reversed, we're pulling in the arguments in reverse, + # so we need to turn them around. + if spos is not None: + x.reverse() + + rv.append(tuple(x)) + elif nargs < 0: + if spos is not None: + raise TypeError("Cannot have two nargs < 0") + + spos = len(rv) + rv.append(None) + + # spos is the position of the wildcard (star). If it's not `None`, + # we fill it with the remainder. + if spos is not None: + rv[spos] = tuple(args) + args = [] + rv[spos + 1 :] = reversed(rv[spos + 1 :]) + + return tuple(rv), list(args) + + +def _split_opt(opt: str) -> tuple[str, str]: + first = opt[:1] + if first.isalnum(): + return "", opt + if opt[1:2] == first: + return opt[:2], opt[2:] + return first, opt[1:] + + +def _normalize_opt(opt: str, ctx: Context | None) -> str: + if ctx is None or ctx.token_normalize_func is None: + return opt + prefix, opt = _split_opt(opt) + return f"{prefix}{ctx.token_normalize_func(opt)}" + + +class _Option: + def __init__( + self, + obj: CoreOption, + opts: cabc.Sequence[str], + dest: str | None, + action: str | None = None, + nargs: int = 1, + const: t.Any | None = None, + ): + self._short_opts = [] + self._long_opts = [] + self.prefixes: set[str] = set() + + for opt in opts: + prefix, value = _split_opt(opt) + if not prefix: + raise ValueError(f"Invalid start character for option ({opt})") + self.prefixes.add(prefix[0]) + if len(prefix) == 1 and len(value) == 1: + self._short_opts.append(opt) + else: + self._long_opts.append(opt) + self.prefixes.add(prefix) + + if action is None: + action = "store" + + self.dest = dest + self.action = action + self.nargs = nargs + self.const = const + self.obj = obj + + @property + def takes_value(self) -> bool: + return self.action in ("store", "append") + + def process(self, value: t.Any, state: _ParsingState) -> None: + if self.action == "store": + state.opts[self.dest] = value # type: ignore + elif self.action == "store_const": + state.opts[self.dest] = self.const # type: ignore + elif self.action == "append": + state.opts.setdefault(self.dest, []).append(value) # type: ignore + elif self.action == "append_const": + state.opts.setdefault(self.dest, []).append(self.const) # type: ignore + elif self.action == "count": + state.opts[self.dest] = state.opts.get(self.dest, 0) + 1 # type: ignore + else: + raise ValueError(f"unknown action '{self.action}'") + state.order.append(self.obj) + + +class _Argument: + def __init__(self, obj: CoreArgument, dest: str | None, nargs: int = 1): + self.dest = dest + self.nargs = nargs + self.obj = obj + + def process( + self, + value: str | cabc.Sequence[str | None] | None, + state: _ParsingState, + ) -> None: + if self.nargs > 1: + assert value is not None + holes = sum(1 for x in value if x is None) + if holes == len(value): + value = None + elif holes != 0: + raise BadArgumentUsage( + _("Argument {name!r} takes {nargs} values.").format( + name=self.dest, nargs=self.nargs + ) + ) + + if self.nargs == -1 and self.obj.envvar is not None and value == (): + # Replace empty tuple with None so that a value from the + # environment may be tried. + value = None + + state.opts[self.dest] = value # type: ignore + state.order.append(self.obj) + + +class _ParsingState: + def __init__(self, rargs: list[str]) -> None: + self.opts: dict[str, t.Any] = {} + self.largs: list[str] = [] + self.rargs = rargs + self.order: list[CoreParameter] = [] + + +class _OptionParser: + """The option parser is an internal class that is ultimately used to + parse options and arguments. It's modelled after optparse and brings + a similar but vastly simplified API. It should generally not be used + directly as the high level Click classes wrap it for you. + + It's not nearly as extensible as optparse or argparse as it does not + implement features that are implemented on a higher level (such as + types or defaults). + + :param ctx: optionally the :class:`~click.Context` where this parser + should go with. + + .. deprecated:: 8.2 + Will be removed in Click 9.0. + """ + + def __init__(self, ctx: Context | None = None) -> None: + #: The :class:`~click.Context` for this parser. This might be + #: `None` for some advanced use cases. + self.ctx = ctx + #: This controls how the parser deals with interspersed arguments. + #: If this is set to `False`, the parser will stop on the first + #: non-option. Click uses this to implement nested subcommands + #: safely. + self.allow_interspersed_args: bool = True + #: This tells the parser how to deal with unknown options. By + #: default it will error out (which is sensible), but there is a + #: second mode where it will ignore it and continue processing + #: after shifting all the unknown options into the resulting args. + self.ignore_unknown_options: bool = False + + if ctx is not None: + self.allow_interspersed_args = ctx.allow_interspersed_args + self.ignore_unknown_options = ctx.ignore_unknown_options + + self._short_opt: dict[str, _Option] = {} + self._long_opt: dict[str, _Option] = {} + self._opt_prefixes = {"-", "--"} + self._args: list[_Argument] = [] + + def add_option( + self, + obj: CoreOption, + opts: cabc.Sequence[str], + dest: str | None, + action: str | None = None, + nargs: int = 1, + const: t.Any | None = None, + ) -> None: + """Adds a new option named `dest` to the parser. The destination + is not inferred (unlike with optparse) and needs to be explicitly + provided. Action can be any of ``store``, ``store_const``, + ``append``, ``append_const`` or ``count``. + + The `obj` can be used to identify the option in the order list + that is returned from the parser. + """ + opts = [_normalize_opt(opt, self.ctx) for opt in opts] + option = _Option(obj, opts, dest, action=action, nargs=nargs, const=const) + self._opt_prefixes.update(option.prefixes) + for opt in option._short_opts: + self._short_opt[opt] = option + for opt in option._long_opts: + self._long_opt[opt] = option + + def add_argument(self, obj: CoreArgument, dest: str | None, nargs: int = 1) -> None: + """Adds a positional argument named `dest` to the parser. + + The `obj` can be used to identify the option in the order list + that is returned from the parser. + """ + self._args.append(_Argument(obj, dest=dest, nargs=nargs)) + + def parse_args( + self, args: list[str] + ) -> tuple[dict[str, t.Any], list[str], list[CoreParameter]]: + """Parses positional arguments and returns ``(values, args, order)`` + for the parsed options and arguments as well as the leftover + arguments if there are any. The order is a list of objects as they + appear on the command line. If arguments appear multiple times they + will be memorized multiple times as well. + """ + state = _ParsingState(args) + try: + self._process_args_for_options(state) + self._process_args_for_args(state) + except UsageError: + if self.ctx is None or not self.ctx.resilient_parsing: + raise + return state.opts, state.largs, state.order + + def _process_args_for_args(self, state: _ParsingState) -> None: + pargs, args = _unpack_args( + state.largs + state.rargs, [x.nargs for x in self._args] + ) + + for idx, arg in enumerate(self._args): + arg.process(pargs[idx], state) + + state.largs = args + state.rargs = [] + + def _process_args_for_options(self, state: _ParsingState) -> None: + while state.rargs: + arg = state.rargs.pop(0) + arglen = len(arg) + # Double dashes always handled explicitly regardless of what + # prefixes are valid. + if arg == "--": + return + elif arg[:1] in self._opt_prefixes and arglen > 1: + self._process_opts(arg, state) + elif self.allow_interspersed_args: + state.largs.append(arg) + else: + state.rargs.insert(0, arg) + return + + # Say this is the original argument list: + # [arg0, arg1, ..., arg(i-1), arg(i), arg(i+1), ..., arg(N-1)] + # ^ + # (we are about to process arg(i)). + # + # Then rargs is [arg(i), ..., arg(N-1)] and largs is a *subset* of + # [arg0, ..., arg(i-1)] (any options and their arguments will have + # been removed from largs). + # + # The while loop will usually consume 1 or more arguments per pass. + # If it consumes 1 (eg. arg is an option that takes no arguments), + # then after _process_arg() is done the situation is: + # + # largs = subset of [arg0, ..., arg(i)] + # rargs = [arg(i+1), ..., arg(N-1)] + # + # If allow_interspersed_args is false, largs will always be + # *empty* -- still a subset of [arg0, ..., arg(i-1)], but + # not a very interesting subset! + + def _match_long_opt( + self, opt: str, explicit_value: str | None, state: _ParsingState + ) -> None: + if opt not in self._long_opt: + from difflib import get_close_matches + + possibilities = get_close_matches(opt, self._long_opt) + raise NoSuchOption(opt, possibilities=possibilities, ctx=self.ctx) + + option = self._long_opt[opt] + if option.takes_value: + # At this point it's safe to modify rargs by injecting the + # explicit value, because no exception is raised in this + # branch. This means that the inserted value will be fully + # consumed. + if explicit_value is not None: + state.rargs.insert(0, explicit_value) + + value = self._get_value_from_state(opt, option, state) + + elif explicit_value is not None: + raise BadOptionUsage( + opt, _("Option {name!r} does not take a value.").format(name=opt) + ) + + else: + value = None + + option.process(value, state) + + def _match_short_opt(self, arg: str, state: _ParsingState) -> None: + stop = False + i = 1 + prefix = arg[0] + unknown_options = [] + + for ch in arg[1:]: + opt = _normalize_opt(f"{prefix}{ch}", self.ctx) + option = self._short_opt.get(opt) + i += 1 + + if not option: + if self.ignore_unknown_options: + unknown_options.append(ch) + continue + raise NoSuchOption(opt, ctx=self.ctx) + if option.takes_value: + # Any characters left in arg? Pretend they're the + # next arg, and stop consuming characters of arg. + if i < len(arg): + state.rargs.insert(0, arg[i:]) + stop = True + + value = self._get_value_from_state(opt, option, state) + + else: + value = None + + option.process(value, state) + + if stop: + break + + # If we got any unknown options we recombine the string of the + # remaining options and re-attach the prefix, then report that + # to the state as new larg. This way there is basic combinatorics + # that can be achieved while still ignoring unknown arguments. + if self.ignore_unknown_options and unknown_options: + state.largs.append(f"{prefix}{''.join(unknown_options)}") + + def _get_value_from_state( + self, option_name: str, option: _Option, state: _ParsingState + ) -> t.Any: + nargs = option.nargs + + if len(state.rargs) < nargs: + if option.obj._flag_needs_value: + # Option allows omitting the value. + value = _flag_needs_value + else: + raise BadOptionUsage( + option_name, + ngettext( + "Option {name!r} requires an argument.", + "Option {name!r} requires {nargs} arguments.", + nargs, + ).format(name=option_name, nargs=nargs), + ) + elif nargs == 1: + next_rarg = state.rargs[0] + + if ( + option.obj._flag_needs_value + and isinstance(next_rarg, str) + and next_rarg[:1] in self._opt_prefixes + and len(next_rarg) > 1 + ): + # The next arg looks like the start of an option, don't + # use it as the value if omitting the value is allowed. + value = _flag_needs_value + else: + value = state.rargs.pop(0) + else: + value = tuple(state.rargs[:nargs]) + del state.rargs[:nargs] + + return value + + def _process_opts(self, arg: str, state: _ParsingState) -> None: + explicit_value = None + # Long option handling happens in two parts. The first part is + # supporting explicitly attached values. In any case, we will try + # to long match the option first. + if "=" in arg: + long_opt, explicit_value = arg.split("=", 1) + else: + long_opt = arg + norm_long_opt = _normalize_opt(long_opt, self.ctx) + + # At this point we will match the (assumed) long option through + # the long option matching code. Note that this allows options + # like "-foo" to be matched as long options. + try: + self._match_long_opt(norm_long_opt, explicit_value, state) + except NoSuchOption: + # At this point the long option matching failed, and we need + # to try with short options. However there is a special rule + # which says, that if we have a two character options prefix + # (applies to "--foo" for instance), we do not dispatch to the + # short option code and will instead raise the no option + # error. + if arg[:2] not in self._opt_prefixes: + self._match_short_opt(arg, state) + return + + if not self.ignore_unknown_options: + raise + + state.largs.append(arg) + + +def __getattr__(name: str) -> object: + import warnings + + if name in { + "OptionParser", + "Argument", + "Option", + "split_opt", + "normalize_opt", + "ParsingState", + }: + warnings.warn( + f"'parser.{name}' is deprecated and will be removed in Click 9.0." + " The old parser is available in 'optparse'.", + DeprecationWarning, + stacklevel=2, + ) + return globals()[f"_{name}"] + + if name == "split_arg_string": + from .shell_completion import split_arg_string + + warnings.warn( + "Importing 'parser.split_arg_string' is deprecated, it will only be" + " available in 'shell_completion' in Click 9.0.", + DeprecationWarning, + stacklevel=2, + ) + return split_arg_string + + raise AttributeError(name) diff --git a/venv/lib/python3.12/site-packages/click/py.typed b/venv/lib/python3.12/site-packages/click/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/venv/lib/python3.12/site-packages/click/shell_completion.py b/venv/lib/python3.12/site-packages/click/shell_completion.py new file mode 100644 index 00000000..6c39d5eb --- /dev/null +++ b/venv/lib/python3.12/site-packages/click/shell_completion.py @@ -0,0 +1,644 @@ +from __future__ import annotations + +import collections.abc as cabc +import os +import re +import typing as t +from gettext import gettext as _ + +from .core import Argument +from .core import Command +from .core import Context +from .core import Group +from .core import Option +from .core import Parameter +from .core import ParameterSource +from .utils import echo + + +def shell_complete( + cli: Command, + ctx_args: cabc.MutableMapping[str, t.Any], + prog_name: str, + complete_var: str, + instruction: str, +) -> int: + """Perform shell completion for the given CLI program. + + :param cli: Command being called. + :param ctx_args: Extra arguments to pass to + ``cli.make_context``. + :param prog_name: Name of the executable in the shell. + :param complete_var: Name of the environment variable that holds + the completion instruction. + :param instruction: Value of ``complete_var`` with the completion + instruction and shell, in the form ``instruction_shell``. + :return: Status code to exit with. + """ + shell, _, instruction = instruction.partition("_") + comp_cls = get_completion_class(shell) + + if comp_cls is None: + return 1 + + comp = comp_cls(cli, ctx_args, prog_name, complete_var) + + if instruction == "source": + echo(comp.source()) + return 0 + + if instruction == "complete": + echo(comp.complete()) + return 0 + + return 1 + + +class CompletionItem: + """Represents a completion value and metadata about the value. The + default metadata is ``type`` to indicate special shell handling, + and ``help`` if a shell supports showing a help string next to the + value. + + Arbitrary parameters can be passed when creating the object, and + accessed using ``item.attr``. If an attribute wasn't passed, + accessing it returns ``None``. + + :param value: The completion suggestion. + :param type: Tells the shell script to provide special completion + support for the type. Click uses ``"dir"`` and ``"file"``. + :param help: String shown next to the value if supported. + :param kwargs: Arbitrary metadata. The built-in implementations + don't use this, but custom type completions paired with custom + shell support could use it. + """ + + __slots__ = ("value", "type", "help", "_info") + + def __init__( + self, + value: t.Any, + type: str = "plain", + help: str | None = None, + **kwargs: t.Any, + ) -> None: + self.value: t.Any = value + self.type: str = type + self.help: str | None = help + self._info = kwargs + + def __getattr__(self, name: str) -> t.Any: + return self._info.get(name) + + +# Only Bash >= 4.4 has the nosort option. +_SOURCE_BASH = """\ +%(complete_func)s() { + local IFS=$'\\n' + local response + + response=$(env COMP_WORDS="${COMP_WORDS[*]}" COMP_CWORD=$COMP_CWORD \ +%(complete_var)s=bash_complete $1) + + for completion in $response; do + IFS=',' read type value <<< "$completion" + + if [[ $type == 'dir' ]]; then + COMPREPLY=() + compopt -o dirnames + elif [[ $type == 'file' ]]; then + COMPREPLY=() + compopt -o default + elif [[ $type == 'plain' ]]; then + COMPREPLY+=($value) + fi + done + + return 0 +} + +%(complete_func)s_setup() { + complete -o nosort -F %(complete_func)s %(prog_name)s +} + +%(complete_func)s_setup; +""" + +_SOURCE_ZSH = """\ +#compdef %(prog_name)s + +%(complete_func)s() { + local -a completions + local -a completions_with_descriptions + local -a response + (( ! $+commands[%(prog_name)s] )) && return 1 + + response=("${(@f)$(env COMP_WORDS="${words[*]}" COMP_CWORD=$((CURRENT-1)) \ +%(complete_var)s=zsh_complete %(prog_name)s)}") + + for type key descr in ${response}; do + if [[ "$type" == "plain" ]]; then + if [[ "$descr" == "_" ]]; then + completions+=("$key") + else + completions_with_descriptions+=("$key":"$descr") + fi + elif [[ "$type" == "dir" ]]; then + _path_files -/ + elif [[ "$type" == "file" ]]; then + _path_files -f + fi + done + + if [ -n "$completions_with_descriptions" ]; then + _describe -V unsorted completions_with_descriptions -U + fi + + if [ -n "$completions" ]; then + compadd -U -V unsorted -a completions + fi +} + +if [[ $zsh_eval_context[-1] == loadautofunc ]]; then + # autoload from fpath, call function directly + %(complete_func)s "$@" +else + # eval/source/. command, register function for later + compdef %(complete_func)s %(prog_name)s +fi +""" + +_SOURCE_FISH = """\ +function %(complete_func)s; + set -l response (env %(complete_var)s=fish_complete COMP_WORDS=(commandline -cp) \ +COMP_CWORD=(commandline -t) %(prog_name)s); + + for completion in $response; + set -l metadata (string split "," $completion); + + if test $metadata[1] = "dir"; + __fish_complete_directories $metadata[2]; + else if test $metadata[1] = "file"; + __fish_complete_path $metadata[2]; + else if test $metadata[1] = "plain"; + echo $metadata[2]; + end; + end; +end; + +complete --no-files --command %(prog_name)s --arguments \ +"(%(complete_func)s)"; +""" + + +class ShellComplete: + """Base class for providing shell completion support. A subclass for + a given shell will override attributes and methods to implement the + completion instructions (``source`` and ``complete``). + + :param cli: Command being called. + :param prog_name: Name of the executable in the shell. + :param complete_var: Name of the environment variable that holds + the completion instruction. + + .. versionadded:: 8.0 + """ + + name: t.ClassVar[str] + """Name to register the shell as with :func:`add_completion_class`. + This is used in completion instructions (``{name}_source`` and + ``{name}_complete``). + """ + + source_template: t.ClassVar[str] + """Completion script template formatted by :meth:`source`. This must + be provided by subclasses. + """ + + def __init__( + self, + cli: Command, + ctx_args: cabc.MutableMapping[str, t.Any], + prog_name: str, + complete_var: str, + ) -> None: + self.cli = cli + self.ctx_args = ctx_args + self.prog_name = prog_name + self.complete_var = complete_var + + @property + def func_name(self) -> str: + """The name of the shell function defined by the completion + script. + """ + safe_name = re.sub(r"\W*", "", self.prog_name.replace("-", "_"), flags=re.ASCII) + return f"_{safe_name}_completion" + + def source_vars(self) -> dict[str, t.Any]: + """Vars for formatting :attr:`source_template`. + + By default this provides ``complete_func``, ``complete_var``, + and ``prog_name``. + """ + return { + "complete_func": self.func_name, + "complete_var": self.complete_var, + "prog_name": self.prog_name, + } + + def source(self) -> str: + """Produce the shell script that defines the completion + function. By default this ``%``-style formats + :attr:`source_template` with the dict returned by + :meth:`source_vars`. + """ + return self.source_template % self.source_vars() + + def get_completion_args(self) -> tuple[list[str], str]: + """Use the env vars defined by the shell script to return a + tuple of ``args, incomplete``. This must be implemented by + subclasses. + """ + raise NotImplementedError + + def get_completions(self, args: list[str], incomplete: str) -> list[CompletionItem]: + """Determine the context and last complete command or parameter + from the complete args. Call that object's ``shell_complete`` + method to get the completions for the incomplete value. + + :param args: List of complete args before the incomplete value. + :param incomplete: Value being completed. May be empty. + """ + ctx = _resolve_context(self.cli, self.ctx_args, self.prog_name, args) + obj, incomplete = _resolve_incomplete(ctx, args, incomplete) + return obj.shell_complete(ctx, incomplete) + + def format_completion(self, item: CompletionItem) -> str: + """Format a completion item into the form recognized by the + shell script. This must be implemented by subclasses. + + :param item: Completion item to format. + """ + raise NotImplementedError + + def complete(self) -> str: + """Produce the completion data to send back to the shell. + + By default this calls :meth:`get_completion_args`, gets the + completions, then calls :meth:`format_completion` for each + completion. + """ + args, incomplete = self.get_completion_args() + completions = self.get_completions(args, incomplete) + out = [self.format_completion(item) for item in completions] + return "\n".join(out) + + +class BashComplete(ShellComplete): + """Shell completion for Bash.""" + + name = "bash" + source_template = _SOURCE_BASH + + @staticmethod + def _check_version() -> None: + import shutil + import subprocess + + bash_exe = shutil.which("bash") + + if bash_exe is None: + match = None + else: + output = subprocess.run( + [bash_exe, "--norc", "-c", 'echo "${BASH_VERSION}"'], + stdout=subprocess.PIPE, + ) + match = re.search(r"^(\d+)\.(\d+)\.\d+", output.stdout.decode()) + + if match is not None: + major, minor = match.groups() + + if major < "4" or major == "4" and minor < "4": + echo( + _( + "Shell completion is not supported for Bash" + " versions older than 4.4." + ), + err=True, + ) + else: + echo( + _("Couldn't detect Bash version, shell completion is not supported."), + err=True, + ) + + def source(self) -> str: + self._check_version() + return super().source() + + def get_completion_args(self) -> tuple[list[str], str]: + cwords = split_arg_string(os.environ["COMP_WORDS"]) + cword = int(os.environ["COMP_CWORD"]) + args = cwords[1:cword] + + try: + incomplete = cwords[cword] + except IndexError: + incomplete = "" + + return args, incomplete + + def format_completion(self, item: CompletionItem) -> str: + return f"{item.type},{item.value}" + + +class ZshComplete(ShellComplete): + """Shell completion for Zsh.""" + + name = "zsh" + source_template = _SOURCE_ZSH + + def get_completion_args(self) -> tuple[list[str], str]: + cwords = split_arg_string(os.environ["COMP_WORDS"]) + cword = int(os.environ["COMP_CWORD"]) + args = cwords[1:cword] + + try: + incomplete = cwords[cword] + except IndexError: + incomplete = "" + + return args, incomplete + + def format_completion(self, item: CompletionItem) -> str: + return f"{item.type}\n{item.value}\n{item.help if item.help else '_'}" + + +class FishComplete(ShellComplete): + """Shell completion for Fish.""" + + name = "fish" + source_template = _SOURCE_FISH + + def get_completion_args(self) -> tuple[list[str], str]: + cwords = split_arg_string(os.environ["COMP_WORDS"]) + incomplete = os.environ["COMP_CWORD"] + args = cwords[1:] + + # Fish stores the partial word in both COMP_WORDS and + # COMP_CWORD, remove it from complete args. + if incomplete and args and args[-1] == incomplete: + args.pop() + + return args, incomplete + + def format_completion(self, item: CompletionItem) -> str: + if item.help: + return f"{item.type},{item.value}\t{item.help}" + + return f"{item.type},{item.value}" + + +ShellCompleteType = t.TypeVar("ShellCompleteType", bound="type[ShellComplete]") + + +_available_shells: dict[str, type[ShellComplete]] = { + "bash": BashComplete, + "fish": FishComplete, + "zsh": ZshComplete, +} + + +def add_completion_class( + cls: ShellCompleteType, name: str | None = None +) -> ShellCompleteType: + """Register a :class:`ShellComplete` subclass under the given name. + The name will be provided by the completion instruction environment + variable during completion. + + :param cls: The completion class that will handle completion for the + shell. + :param name: Name to register the class under. Defaults to the + class's ``name`` attribute. + """ + if name is None: + name = cls.name + + _available_shells[name] = cls + + return cls + + +def get_completion_class(shell: str) -> type[ShellComplete] | None: + """Look up a registered :class:`ShellComplete` subclass by the name + provided by the completion instruction environment variable. If the + name isn't registered, returns ``None``. + + :param shell: Name the class is registered under. + """ + return _available_shells.get(shell) + + +def split_arg_string(string: str) -> list[str]: + """Split an argument string as with :func:`shlex.split`, but don't + fail if the string is incomplete. Ignores a missing closing quote or + incomplete escape sequence and uses the partial token as-is. + + .. code-block:: python + + split_arg_string("example 'my file") + ["example", "my file"] + + split_arg_string("example my\\") + ["example", "my"] + + :param string: String to split. + + .. versionchanged:: 8.2 + Moved to ``shell_completion`` from ``parser``. + """ + import shlex + + lex = shlex.shlex(string, posix=True) + lex.whitespace_split = True + lex.commenters = "" + out = [] + + try: + for token in lex: + out.append(token) + except ValueError: + # Raised when end-of-string is reached in an invalid state. Use + # the partial token as-is. The quote or escape character is in + # lex.state, not lex.token. + out.append(lex.token) + + return out + + +def _is_incomplete_argument(ctx: Context, param: Parameter) -> bool: + """Determine if the given parameter is an argument that can still + accept values. + + :param ctx: Invocation context for the command represented by the + parsed complete args. + :param param: Argument object being checked. + """ + if not isinstance(param, Argument): + return False + + assert param.name is not None + # Will be None if expose_value is False. + value = ctx.params.get(param.name) + return ( + param.nargs == -1 + or ctx.get_parameter_source(param.name) is not ParameterSource.COMMANDLINE + or ( + param.nargs > 1 + and isinstance(value, (tuple, list)) + and len(value) < param.nargs + ) + ) + + +def _start_of_option(ctx: Context, value: str) -> bool: + """Check if the value looks like the start of an option.""" + if not value: + return False + + c = value[0] + return c in ctx._opt_prefixes + + +def _is_incomplete_option(ctx: Context, args: list[str], param: Parameter) -> bool: + """Determine if the given parameter is an option that needs a value. + + :param args: List of complete args before the incomplete value. + :param param: Option object being checked. + """ + if not isinstance(param, Option): + return False + + if param.is_flag or param.count: + return False + + last_option = None + + for index, arg in enumerate(reversed(args)): + if index + 1 > param.nargs: + break + + if _start_of_option(ctx, arg): + last_option = arg + + return last_option is not None and last_option in param.opts + + +def _resolve_context( + cli: Command, + ctx_args: cabc.MutableMapping[str, t.Any], + prog_name: str, + args: list[str], +) -> Context: + """Produce the context hierarchy starting with the command and + traversing the complete arguments. This only follows the commands, + it doesn't trigger input prompts or callbacks. + + :param cli: Command being called. + :param prog_name: Name of the executable in the shell. + :param args: List of complete args before the incomplete value. + """ + ctx_args["resilient_parsing"] = True + with cli.make_context(prog_name, args.copy(), **ctx_args) as ctx: + args = ctx._protected_args + ctx.args + + while args: + command = ctx.command + + if isinstance(command, Group): + if not command.chain: + name, cmd, args = command.resolve_command(ctx, args) + + if cmd is None: + return ctx + + with cmd.make_context( + name, args, parent=ctx, resilient_parsing=True + ) as sub_ctx: + ctx = sub_ctx + args = ctx._protected_args + ctx.args + else: + sub_ctx = ctx + + while args: + name, cmd, args = command.resolve_command(ctx, args) + + if cmd is None: + return ctx + + with cmd.make_context( + name, + args, + parent=ctx, + allow_extra_args=True, + allow_interspersed_args=False, + resilient_parsing=True, + ) as sub_sub_ctx: + sub_ctx = sub_sub_ctx + args = sub_ctx.args + + ctx = sub_ctx + args = [*sub_ctx._protected_args, *sub_ctx.args] + else: + break + + return ctx + + +def _resolve_incomplete( + ctx: Context, args: list[str], incomplete: str +) -> tuple[Command | Parameter, str]: + """Find the Click object that will handle the completion of the + incomplete value. Return the object and the incomplete value. + + :param ctx: Invocation context for the command represented by + the parsed complete args. + :param args: List of complete args before the incomplete value. + :param incomplete: Value being completed. May be empty. + """ + # Different shells treat an "=" between a long option name and + # value differently. Might keep the value joined, return the "=" + # as a separate item, or return the split name and value. Always + # split and discard the "=" to make completion easier. + if incomplete == "=": + incomplete = "" + elif "=" in incomplete and _start_of_option(ctx, incomplete): + name, _, incomplete = incomplete.partition("=") + args.append(name) + + # The "--" marker tells Click to stop treating values as options + # even if they start with the option character. If it hasn't been + # given and the incomplete arg looks like an option, the current + # command will provide option name completions. + if "--" not in args and _start_of_option(ctx, incomplete): + return ctx.command, incomplete + + params = ctx.command.get_params(ctx) + + # If the last complete arg is an option name with an incomplete + # value, the option will provide value completions. + for param in params: + if _is_incomplete_option(ctx, args, param): + return param, incomplete + + # It's not an option name or value. The first argument without a + # parsed value will provide value completions. + for param in params: + if _is_incomplete_argument(ctx, param): + return param, incomplete + + # There were no unparsed arguments, the command may be a group that + # will provide command name completions. + return ctx.command, incomplete diff --git a/venv/lib/python3.12/site-packages/click/termui.py b/venv/lib/python3.12/site-packages/click/termui.py new file mode 100644 index 00000000..dcbb2221 --- /dev/null +++ b/venv/lib/python3.12/site-packages/click/termui.py @@ -0,0 +1,877 @@ +from __future__ import annotations + +import collections.abc as cabc +import inspect +import io +import itertools +import sys +import typing as t +from contextlib import AbstractContextManager +from gettext import gettext as _ + +from ._compat import isatty +from ._compat import strip_ansi +from .exceptions import Abort +from .exceptions import UsageError +from .globals import resolve_color_default +from .types import Choice +from .types import convert_type +from .types import ParamType +from .utils import echo +from .utils import LazyFile + +if t.TYPE_CHECKING: + from ._termui_impl import ProgressBar + +V = t.TypeVar("V") + +# The prompt functions to use. The doc tools currently override these +# functions to customize how they work. +visible_prompt_func: t.Callable[[str], str] = input + +_ansi_colors = { + "black": 30, + "red": 31, + "green": 32, + "yellow": 33, + "blue": 34, + "magenta": 35, + "cyan": 36, + "white": 37, + "reset": 39, + "bright_black": 90, + "bright_red": 91, + "bright_green": 92, + "bright_yellow": 93, + "bright_blue": 94, + "bright_magenta": 95, + "bright_cyan": 96, + "bright_white": 97, +} +_ansi_reset_all = "\033[0m" + + +def hidden_prompt_func(prompt: str) -> str: + import getpass + + return getpass.getpass(prompt) + + +def _build_prompt( + text: str, + suffix: str, + show_default: bool = False, + default: t.Any | None = None, + show_choices: bool = True, + type: ParamType | None = None, +) -> str: + prompt = text + if type is not None and show_choices and isinstance(type, Choice): + prompt += f" ({', '.join(map(str, type.choices))})" + if default is not None and show_default: + prompt = f"{prompt} [{_format_default(default)}]" + return f"{prompt}{suffix}" + + +def _format_default(default: t.Any) -> t.Any: + if isinstance(default, (io.IOBase, LazyFile)) and hasattr(default, "name"): + return default.name + + return default + + +def prompt( + text: str, + default: t.Any | None = None, + hide_input: bool = False, + confirmation_prompt: bool | str = False, + type: ParamType | t.Any | None = None, + value_proc: t.Callable[[str], t.Any] | None = None, + prompt_suffix: str = ": ", + show_default: bool = True, + err: bool = False, + show_choices: bool = True, +) -> t.Any: + """Prompts a user for input. This is a convenience function that can + be used to prompt a user for input later. + + If the user aborts the input by sending an interrupt signal, this + function will catch it and raise a :exc:`Abort` exception. + + :param text: the text to show for the prompt. + :param default: the default value to use if no input happens. If this + is not given it will prompt until it's aborted. + :param hide_input: if this is set to true then the input value will + be hidden. + :param confirmation_prompt: Prompt a second time to confirm the + value. Can be set to a string instead of ``True`` to customize + the message. + :param type: the type to use to check the value against. + :param value_proc: if this parameter is provided it's a function that + is invoked instead of the type conversion to + convert a value. + :param prompt_suffix: a suffix that should be added to the prompt. + :param show_default: shows or hides the default value in the prompt. + :param err: if set to true the file defaults to ``stderr`` instead of + ``stdout``, the same as with echo. + :param show_choices: Show or hide choices if the passed type is a Choice. + For example if type is a Choice of either day or week, + show_choices is true and text is "Group by" then the + prompt will be "Group by (day, week): ". + + .. versionadded:: 8.0 + ``confirmation_prompt`` can be a custom string. + + .. versionadded:: 7.0 + Added the ``show_choices`` parameter. + + .. versionadded:: 6.0 + Added unicode support for cmd.exe on Windows. + + .. versionadded:: 4.0 + Added the `err` parameter. + + """ + + def prompt_func(text: str) -> str: + f = hidden_prompt_func if hide_input else visible_prompt_func + try: + # Write the prompt separately so that we get nice + # coloring through colorama on Windows + echo(text.rstrip(" "), nl=False, err=err) + # Echo a space to stdout to work around an issue where + # readline causes backspace to clear the whole line. + return f(" ") + except (KeyboardInterrupt, EOFError): + # getpass doesn't print a newline if the user aborts input with ^C. + # Allegedly this behavior is inherited from getpass(3). + # A doc bug has been filed at https://bugs.python.org/issue24711 + if hide_input: + echo(None, err=err) + raise Abort() from None + + if value_proc is None: + value_proc = convert_type(type, default) + + prompt = _build_prompt( + text, prompt_suffix, show_default, default, show_choices, type + ) + + if confirmation_prompt: + if confirmation_prompt is True: + confirmation_prompt = _("Repeat for confirmation") + + confirmation_prompt = _build_prompt(confirmation_prompt, prompt_suffix) + + while True: + while True: + value = prompt_func(prompt) + if value: + break + elif default is not None: + value = default + break + try: + result = value_proc(value) + except UsageError as e: + if hide_input: + echo(_("Error: The value you entered was invalid."), err=err) + else: + echo(_("Error: {e.message}").format(e=e), err=err) + continue + if not confirmation_prompt: + return result + while True: + value2 = prompt_func(confirmation_prompt) + is_empty = not value and not value2 + if value2 or is_empty: + break + if value == value2: + return result + echo(_("Error: The two entered values do not match."), err=err) + + +def confirm( + text: str, + default: bool | None = False, + abort: bool = False, + prompt_suffix: str = ": ", + show_default: bool = True, + err: bool = False, +) -> bool: + """Prompts for confirmation (yes/no question). + + If the user aborts the input by sending a interrupt signal this + function will catch it and raise a :exc:`Abort` exception. + + :param text: the question to ask. + :param default: The default value to use when no input is given. If + ``None``, repeat until input is given. + :param abort: if this is set to `True` a negative answer aborts the + exception by raising :exc:`Abort`. + :param prompt_suffix: a suffix that should be added to the prompt. + :param show_default: shows or hides the default value in the prompt. + :param err: if set to true the file defaults to ``stderr`` instead of + ``stdout``, the same as with echo. + + .. versionchanged:: 8.0 + Repeat until input is given if ``default`` is ``None``. + + .. versionadded:: 4.0 + Added the ``err`` parameter. + """ + prompt = _build_prompt( + text, + prompt_suffix, + show_default, + "y/n" if default is None else ("Y/n" if default else "y/N"), + ) + + while True: + try: + # Write the prompt separately so that we get nice + # coloring through colorama on Windows + echo(prompt.rstrip(" "), nl=False, err=err) + # Echo a space to stdout to work around an issue where + # readline causes backspace to clear the whole line. + value = visible_prompt_func(" ").lower().strip() + except (KeyboardInterrupt, EOFError): + raise Abort() from None + if value in ("y", "yes"): + rv = True + elif value in ("n", "no"): + rv = False + elif default is not None and value == "": + rv = default + else: + echo(_("Error: invalid input"), err=err) + continue + break + if abort and not rv: + raise Abort() + return rv + + +def echo_via_pager( + text_or_generator: cabc.Iterable[str] | t.Callable[[], cabc.Iterable[str]] | str, + color: bool | None = None, +) -> None: + """This function takes a text and shows it via an environment specific + pager on stdout. + + .. versionchanged:: 3.0 + Added the `color` flag. + + :param text_or_generator: the text to page, or alternatively, a + generator emitting the text to page. + :param color: controls if the pager supports ANSI colors or not. The + default is autodetection. + """ + color = resolve_color_default(color) + + if inspect.isgeneratorfunction(text_or_generator): + i = t.cast("t.Callable[[], cabc.Iterable[str]]", text_or_generator)() + elif isinstance(text_or_generator, str): + i = [text_or_generator] + else: + i = iter(t.cast("cabc.Iterable[str]", text_or_generator)) + + # convert every element of i to a text type if necessary + text_generator = (el if isinstance(el, str) else str(el) for el in i) + + from ._termui_impl import pager + + return pager(itertools.chain(text_generator, "\n"), color) + + +@t.overload +def progressbar( + *, + length: int, + label: str | None = None, + hidden: bool = False, + show_eta: bool = True, + show_percent: bool | None = None, + show_pos: bool = False, + fill_char: str = "#", + empty_char: str = "-", + bar_template: str = "%(label)s [%(bar)s] %(info)s", + info_sep: str = " ", + width: int = 36, + file: t.TextIO | None = None, + color: bool | None = None, + update_min_steps: int = 1, +) -> ProgressBar[int]: ... + + +@t.overload +def progressbar( + iterable: cabc.Iterable[V] | None = None, + length: int | None = None, + label: str | None = None, + hidden: bool = False, + show_eta: bool = True, + show_percent: bool | None = None, + show_pos: bool = False, + item_show_func: t.Callable[[V | None], str | None] | None = None, + fill_char: str = "#", + empty_char: str = "-", + bar_template: str = "%(label)s [%(bar)s] %(info)s", + info_sep: str = " ", + width: int = 36, + file: t.TextIO | None = None, + color: bool | None = None, + update_min_steps: int = 1, +) -> ProgressBar[V]: ... + + +def progressbar( + iterable: cabc.Iterable[V] | None = None, + length: int | None = None, + label: str | None = None, + hidden: bool = False, + show_eta: bool = True, + show_percent: bool | None = None, + show_pos: bool = False, + item_show_func: t.Callable[[V | None], str | None] | None = None, + fill_char: str = "#", + empty_char: str = "-", + bar_template: str = "%(label)s [%(bar)s] %(info)s", + info_sep: str = " ", + width: int = 36, + file: t.TextIO | None = None, + color: bool | None = None, + update_min_steps: int = 1, +) -> ProgressBar[V]: + """This function creates an iterable context manager that can be used + to iterate over something while showing a progress bar. It will + either iterate over the `iterable` or `length` items (that are counted + up). While iteration happens, this function will print a rendered + progress bar to the given `file` (defaults to stdout) and will attempt + to calculate remaining time and more. By default, this progress bar + will not be rendered if the file is not a terminal. + + The context manager creates the progress bar. When the context + manager is entered the progress bar is already created. With every + iteration over the progress bar, the iterable passed to the bar is + advanced and the bar is updated. When the context manager exits, + a newline is printed and the progress bar is finalized on screen. + + Note: The progress bar is currently designed for use cases where the + total progress can be expected to take at least several seconds. + Because of this, the ProgressBar class object won't display + progress that is considered too fast, and progress where the time + between steps is less than a second. + + No printing must happen or the progress bar will be unintentionally + destroyed. + + Example usage:: + + with progressbar(items) as bar: + for item in bar: + do_something_with(item) + + Alternatively, if no iterable is specified, one can manually update the + progress bar through the `update()` method instead of directly + iterating over the progress bar. The update method accepts the number + of steps to increment the bar with:: + + with progressbar(length=chunks.total_bytes) as bar: + for chunk in chunks: + process_chunk(chunk) + bar.update(chunks.bytes) + + The ``update()`` method also takes an optional value specifying the + ``current_item`` at the new position. This is useful when used + together with ``item_show_func`` to customize the output for each + manual step:: + + with click.progressbar( + length=total_size, + label='Unzipping archive', + item_show_func=lambda a: a.filename + ) as bar: + for archive in zip_file: + archive.extract() + bar.update(archive.size, archive) + + :param iterable: an iterable to iterate over. If not provided the length + is required. + :param length: the number of items to iterate over. By default the + progressbar will attempt to ask the iterator about its + length, which might or might not work. If an iterable is + also provided this parameter can be used to override the + length. If an iterable is not provided the progress bar + will iterate over a range of that length. + :param label: the label to show next to the progress bar. + :param hidden: hide the progressbar. Defaults to ``False``. When no tty is + detected, it will only print the progressbar label. Setting this to + ``False`` also disables that. + :param show_eta: enables or disables the estimated time display. This is + automatically disabled if the length cannot be + determined. + :param show_percent: enables or disables the percentage display. The + default is `True` if the iterable has a length or + `False` if not. + :param show_pos: enables or disables the absolute position display. The + default is `False`. + :param item_show_func: A function called with the current item which + can return a string to show next to the progress bar. If the + function returns ``None`` nothing is shown. The current item can + be ``None``, such as when entering and exiting the bar. + :param fill_char: the character to use to show the filled part of the + progress bar. + :param empty_char: the character to use to show the non-filled part of + the progress bar. + :param bar_template: the format string to use as template for the bar. + The parameters in it are ``label`` for the label, + ``bar`` for the progress bar and ``info`` for the + info section. + :param info_sep: the separator between multiple info items (eta etc.) + :param width: the width of the progress bar in characters, 0 means full + terminal width + :param file: The file to write to. If this is not a terminal then + only the label is printed. + :param color: controls if the terminal supports ANSI colors or not. The + default is autodetection. This is only needed if ANSI + codes are included anywhere in the progress bar output + which is not the case by default. + :param update_min_steps: Render only when this many updates have + completed. This allows tuning for very fast iterators. + + .. versionadded:: 8.2 + The ``hidden`` argument. + + .. versionchanged:: 8.0 + Output is shown even if execution time is less than 0.5 seconds. + + .. versionchanged:: 8.0 + ``item_show_func`` shows the current item, not the previous one. + + .. versionchanged:: 8.0 + Labels are echoed if the output is not a TTY. Reverts a change + in 7.0 that removed all output. + + .. versionadded:: 8.0 + The ``update_min_steps`` parameter. + + .. versionadded:: 4.0 + The ``color`` parameter and ``update`` method. + + .. versionadded:: 2.0 + """ + from ._termui_impl import ProgressBar + + color = resolve_color_default(color) + return ProgressBar( + iterable=iterable, + length=length, + hidden=hidden, + show_eta=show_eta, + show_percent=show_percent, + show_pos=show_pos, + item_show_func=item_show_func, + fill_char=fill_char, + empty_char=empty_char, + bar_template=bar_template, + info_sep=info_sep, + file=file, + label=label, + width=width, + color=color, + update_min_steps=update_min_steps, + ) + + +def clear() -> None: + """Clears the terminal screen. This will have the effect of clearing + the whole visible space of the terminal and moving the cursor to the + top left. This does not do anything if not connected to a terminal. + + .. versionadded:: 2.0 + """ + if not isatty(sys.stdout): + return + + # ANSI escape \033[2J clears the screen, \033[1;1H moves the cursor + echo("\033[2J\033[1;1H", nl=False) + + +def _interpret_color(color: int | tuple[int, int, int] | str, offset: int = 0) -> str: + if isinstance(color, int): + return f"{38 + offset};5;{color:d}" + + if isinstance(color, (tuple, list)): + r, g, b = color + return f"{38 + offset};2;{r:d};{g:d};{b:d}" + + return str(_ansi_colors[color] + offset) + + +def style( + text: t.Any, + fg: int | tuple[int, int, int] | str | None = None, + bg: int | tuple[int, int, int] | str | None = None, + bold: bool | None = None, + dim: bool | None = None, + underline: bool | None = None, + overline: bool | None = None, + italic: bool | None = None, + blink: bool | None = None, + reverse: bool | None = None, + strikethrough: bool | None = None, + reset: bool = True, +) -> str: + """Styles a text with ANSI styles and returns the new string. By + default the styling is self contained which means that at the end + of the string a reset code is issued. This can be prevented by + passing ``reset=False``. + + Examples:: + + click.echo(click.style('Hello World!', fg='green')) + click.echo(click.style('ATTENTION!', blink=True)) + click.echo(click.style('Some things', reverse=True, fg='cyan')) + click.echo(click.style('More colors', fg=(255, 12, 128), bg=117)) + + Supported color names: + + * ``black`` (might be a gray) + * ``red`` + * ``green`` + * ``yellow`` (might be an orange) + * ``blue`` + * ``magenta`` + * ``cyan`` + * ``white`` (might be light gray) + * ``bright_black`` + * ``bright_red`` + * ``bright_green`` + * ``bright_yellow`` + * ``bright_blue`` + * ``bright_magenta`` + * ``bright_cyan`` + * ``bright_white`` + * ``reset`` (reset the color code only) + + If the terminal supports it, color may also be specified as: + + - An integer in the interval [0, 255]. The terminal must support + 8-bit/256-color mode. + - An RGB tuple of three integers in [0, 255]. The terminal must + support 24-bit/true-color mode. + + See https://en.wikipedia.org/wiki/ANSI_color and + https://gist.github.com/XVilka/8346728 for more information. + + :param text: the string to style with ansi codes. + :param fg: if provided this will become the foreground color. + :param bg: if provided this will become the background color. + :param bold: if provided this will enable or disable bold mode. + :param dim: if provided this will enable or disable dim mode. This is + badly supported. + :param underline: if provided this will enable or disable underline. + :param overline: if provided this will enable or disable overline. + :param italic: if provided this will enable or disable italic. + :param blink: if provided this will enable or disable blinking. + :param reverse: if provided this will enable or disable inverse + rendering (foreground becomes background and the + other way round). + :param strikethrough: if provided this will enable or disable + striking through text. + :param reset: by default a reset-all code is added at the end of the + string which means that styles do not carry over. This + can be disabled to compose styles. + + .. versionchanged:: 8.0 + A non-string ``message`` is converted to a string. + + .. versionchanged:: 8.0 + Added support for 256 and RGB color codes. + + .. versionchanged:: 8.0 + Added the ``strikethrough``, ``italic``, and ``overline`` + parameters. + + .. versionchanged:: 7.0 + Added support for bright colors. + + .. versionadded:: 2.0 + """ + if not isinstance(text, str): + text = str(text) + + bits = [] + + if fg: + try: + bits.append(f"\033[{_interpret_color(fg)}m") + except KeyError: + raise TypeError(f"Unknown color {fg!r}") from None + + if bg: + try: + bits.append(f"\033[{_interpret_color(bg, 10)}m") + except KeyError: + raise TypeError(f"Unknown color {bg!r}") from None + + if bold is not None: + bits.append(f"\033[{1 if bold else 22}m") + if dim is not None: + bits.append(f"\033[{2 if dim else 22}m") + if underline is not None: + bits.append(f"\033[{4 if underline else 24}m") + if overline is not None: + bits.append(f"\033[{53 if overline else 55}m") + if italic is not None: + bits.append(f"\033[{3 if italic else 23}m") + if blink is not None: + bits.append(f"\033[{5 if blink else 25}m") + if reverse is not None: + bits.append(f"\033[{7 if reverse else 27}m") + if strikethrough is not None: + bits.append(f"\033[{9 if strikethrough else 29}m") + bits.append(text) + if reset: + bits.append(_ansi_reset_all) + return "".join(bits) + + +def unstyle(text: str) -> str: + """Removes ANSI styling information from a string. Usually it's not + necessary to use this function as Click's echo function will + automatically remove styling if necessary. + + .. versionadded:: 2.0 + + :param text: the text to remove style information from. + """ + return strip_ansi(text) + + +def secho( + message: t.Any | None = None, + file: t.IO[t.AnyStr] | None = None, + nl: bool = True, + err: bool = False, + color: bool | None = None, + **styles: t.Any, +) -> None: + """This function combines :func:`echo` and :func:`style` into one + call. As such the following two calls are the same:: + + click.secho('Hello World!', fg='green') + click.echo(click.style('Hello World!', fg='green')) + + All keyword arguments are forwarded to the underlying functions + depending on which one they go with. + + Non-string types will be converted to :class:`str`. However, + :class:`bytes` are passed directly to :meth:`echo` without applying + style. If you want to style bytes that represent text, call + :meth:`bytes.decode` first. + + .. versionchanged:: 8.0 + A non-string ``message`` is converted to a string. Bytes are + passed through without style applied. + + .. versionadded:: 2.0 + """ + if message is not None and not isinstance(message, (bytes, bytearray)): + message = style(message, **styles) + + return echo(message, file=file, nl=nl, err=err, color=color) + + +@t.overload +def edit( + text: bytes | bytearray, + editor: str | None = None, + env: cabc.Mapping[str, str] | None = None, + require_save: bool = False, + extension: str = ".txt", +) -> bytes | None: ... + + +@t.overload +def edit( + text: str, + editor: str | None = None, + env: cabc.Mapping[str, str] | None = None, + require_save: bool = True, + extension: str = ".txt", +) -> str | None: ... + + +@t.overload +def edit( + text: None = None, + editor: str | None = None, + env: cabc.Mapping[str, str] | None = None, + require_save: bool = True, + extension: str = ".txt", + filename: str | cabc.Iterable[str] | None = None, +) -> None: ... + + +def edit( + text: str | bytes | bytearray | None = None, + editor: str | None = None, + env: cabc.Mapping[str, str] | None = None, + require_save: bool = True, + extension: str = ".txt", + filename: str | cabc.Iterable[str] | None = None, +) -> str | bytes | bytearray | None: + r"""Edits the given text in the defined editor. If an editor is given + (should be the full path to the executable but the regular operating + system search path is used for finding the executable) it overrides + the detected editor. Optionally, some environment variables can be + used. If the editor is closed without changes, `None` is returned. In + case a file is edited directly the return value is always `None` and + `require_save` and `extension` are ignored. + + If the editor cannot be opened a :exc:`UsageError` is raised. + + Note for Windows: to simplify cross-platform usage, the newlines are + automatically converted from POSIX to Windows and vice versa. As such, + the message here will have ``\n`` as newline markers. + + :param text: the text to edit. + :param editor: optionally the editor to use. Defaults to automatic + detection. + :param env: environment variables to forward to the editor. + :param require_save: if this is true, then not saving in the editor + will make the return value become `None`. + :param extension: the extension to tell the editor about. This defaults + to `.txt` but changing this might change syntax + highlighting. + :param filename: if provided it will edit this file instead of the + provided text contents. It will not use a temporary + file as an indirection in that case. If the editor supports + editing multiple files at once, a sequence of files may be + passed as well. Invoke `click.file` once per file instead + if multiple files cannot be managed at once or editing the + files serially is desired. + + .. versionchanged:: 8.2.0 + ``filename`` now accepts any ``Iterable[str]`` in addition to a ``str`` + if the ``editor`` supports editing multiple files at once. + + """ + from ._termui_impl import Editor + + ed = Editor(editor=editor, env=env, require_save=require_save, extension=extension) + + if filename is None: + return ed.edit(text) + + if isinstance(filename, str): + filename = (filename,) + + ed.edit_files(filenames=filename) + return None + + +def launch(url: str, wait: bool = False, locate: bool = False) -> int: + """This function launches the given URL (or filename) in the default + viewer application for this file type. If this is an executable, it + might launch the executable in a new session. The return value is + the exit code of the launched application. Usually, ``0`` indicates + success. + + Examples:: + + click.launch('https://click.palletsprojects.com/') + click.launch('/my/downloaded/file', locate=True) + + .. versionadded:: 2.0 + + :param url: URL or filename of the thing to launch. + :param wait: Wait for the program to exit before returning. This + only works if the launched program blocks. In particular, + ``xdg-open`` on Linux does not block. + :param locate: if this is set to `True` then instead of launching the + application associated with the URL it will attempt to + launch a file manager with the file located. This + might have weird effects if the URL does not point to + the filesystem. + """ + from ._termui_impl import open_url + + return open_url(url, wait=wait, locate=locate) + + +# If this is provided, getchar() calls into this instead. This is used +# for unittesting purposes. +_getchar: t.Callable[[bool], str] | None = None + + +def getchar(echo: bool = False) -> str: + """Fetches a single character from the terminal and returns it. This + will always return a unicode character and under certain rare + circumstances this might return more than one character. The + situations which more than one character is returned is when for + whatever reason multiple characters end up in the terminal buffer or + standard input was not actually a terminal. + + Note that this will always read from the terminal, even if something + is piped into the standard input. + + Note for Windows: in rare cases when typing non-ASCII characters, this + function might wait for a second character and then return both at once. + This is because certain Unicode characters look like special-key markers. + + .. versionadded:: 2.0 + + :param echo: if set to `True`, the character read will also show up on + the terminal. The default is to not show it. + """ + global _getchar + + if _getchar is None: + from ._termui_impl import getchar as f + + _getchar = f + + return _getchar(echo) + + +def raw_terminal() -> AbstractContextManager[int]: + from ._termui_impl import raw_terminal as f + + return f() + + +def pause(info: str | None = None, err: bool = False) -> None: + """This command stops execution and waits for the user to press any + key to continue. This is similar to the Windows batch "pause" + command. If the program is not run through a terminal, this command + will instead do nothing. + + .. versionadded:: 2.0 + + .. versionadded:: 4.0 + Added the `err` parameter. + + :param info: The message to print before pausing. Defaults to + ``"Press any key to continue..."``. + :param err: if set to message goes to ``stderr`` instead of + ``stdout``, the same as with echo. + """ + if not isatty(sys.stdin) or not isatty(sys.stdout): + return + + if info is None: + info = _("Press any key to continue...") + + try: + if info: + echo(info, nl=False, err=err) + try: + getchar() + except (KeyboardInterrupt, EOFError): + pass + finally: + if info: + echo(err=err) diff --git a/venv/lib/python3.12/site-packages/click/testing.py b/venv/lib/python3.12/site-packages/click/testing.py new file mode 100644 index 00000000..7c0e8741 --- /dev/null +++ b/venv/lib/python3.12/site-packages/click/testing.py @@ -0,0 +1,565 @@ +from __future__ import annotations + +import collections.abc as cabc +import contextlib +import io +import os +import shlex +import shutil +import sys +import tempfile +import typing as t +from types import TracebackType + +from . import _compat +from . import formatting +from . import termui +from . import utils +from ._compat import _find_binary_reader + +if t.TYPE_CHECKING: + from _typeshed import ReadableBuffer + + from .core import Command + + +class EchoingStdin: + def __init__(self, input: t.BinaryIO, output: t.BinaryIO) -> None: + self._input = input + self._output = output + self._paused = False + + def __getattr__(self, x: str) -> t.Any: + return getattr(self._input, x) + + def _echo(self, rv: bytes) -> bytes: + if not self._paused: + self._output.write(rv) + + return rv + + def read(self, n: int = -1) -> bytes: + return self._echo(self._input.read(n)) + + def read1(self, n: int = -1) -> bytes: + return self._echo(self._input.read1(n)) # type: ignore + + def readline(self, n: int = -1) -> bytes: + return self._echo(self._input.readline(n)) + + def readlines(self) -> list[bytes]: + return [self._echo(x) for x in self._input.readlines()] + + def __iter__(self) -> cabc.Iterator[bytes]: + return iter(self._echo(x) for x in self._input) + + def __repr__(self) -> str: + return repr(self._input) + + +@contextlib.contextmanager +def _pause_echo(stream: EchoingStdin | None) -> cabc.Iterator[None]: + if stream is None: + yield + else: + stream._paused = True + yield + stream._paused = False + + +class BytesIOCopy(io.BytesIO): + """Patch ``io.BytesIO`` to let the written stream be copied to another. + + .. versionadded:: 8.2 + """ + + def __init__(self, copy_to: io.BytesIO) -> None: + super().__init__() + self.copy_to = copy_to + + def flush(self) -> None: + super().flush() + self.copy_to.flush() + + def write(self, b: ReadableBuffer) -> int: + self.copy_to.write(b) + return super().write(b) + + +class StreamMixer: + """Mixes `` and `` streams. + + The result is available in the ``output`` attribute. + + .. versionadded:: 8.2 + """ + + def __init__(self) -> None: + self.output: io.BytesIO = io.BytesIO() + self.stdout: io.BytesIO = BytesIOCopy(copy_to=self.output) + self.stderr: io.BytesIO = BytesIOCopy(copy_to=self.output) + + +class _NamedTextIOWrapper(io.TextIOWrapper): + def __init__( + self, buffer: t.BinaryIO, name: str, mode: str, **kwargs: t.Any + ) -> None: + super().__init__(buffer, **kwargs) + self._name = name + self._mode = mode + + @property + def name(self) -> str: + return self._name + + @property + def mode(self) -> str: + return self._mode + + def __next__(self) -> str: # type: ignore + try: + line = super().__next__() + except StopIteration as e: + raise EOFError() from e + return line + + +def make_input_stream( + input: str | bytes | t.IO[t.Any] | None, charset: str +) -> t.BinaryIO: + # Is already an input stream. + if hasattr(input, "read"): + rv = _find_binary_reader(t.cast("t.IO[t.Any]", input)) + + if rv is not None: + return rv + + raise TypeError("Could not find binary reader for input stream.") + + if input is None: + input = b"" + elif isinstance(input, str): + input = input.encode(charset) + + return io.BytesIO(input) + + +class Result: + """Holds the captured result of an invoked CLI script. + + :param runner: The runner that created the result + :param stdout_bytes: The standard output as bytes. + :param stderr_bytes: The standard error as bytes. + :param output_bytes: A mix of ``stdout_bytes`` and ``stderr_bytes``, as the + user would see it in its terminal. + :param return_value: The value returned from the invoked command. + :param exit_code: The exit code as integer. + :param exception: The exception that happened if one did. + :param exc_info: Exception information (exception type, exception instance, + traceback type). + + .. versionchanged:: 8.2 + ``stderr_bytes`` no longer optional, ``output_bytes`` introduced and + ``mix_stderr`` has been removed. + + .. versionadded:: 8.0 + Added ``return_value``. + """ + + def __init__( + self, + runner: CliRunner, + stdout_bytes: bytes, + stderr_bytes: bytes, + output_bytes: bytes, + return_value: t.Any, + exit_code: int, + exception: BaseException | None, + exc_info: tuple[type[BaseException], BaseException, TracebackType] + | None = None, + ): + self.runner = runner + self.stdout_bytes = stdout_bytes + self.stderr_bytes = stderr_bytes + self.output_bytes = output_bytes + self.return_value = return_value + self.exit_code = exit_code + self.exception = exception + self.exc_info = exc_info + + @property + def output(self) -> str: + """The terminal output as unicode string, as the user would see it. + + .. versionchanged:: 8.2 + No longer a proxy for ``self.stdout``. Now has its own independent stream + that is mixing `` and ``, in the order they were written. + """ + return self.output_bytes.decode(self.runner.charset, "replace").replace( + "\r\n", "\n" + ) + + @property + def stdout(self) -> str: + """The standard output as unicode string.""" + return self.stdout_bytes.decode(self.runner.charset, "replace").replace( + "\r\n", "\n" + ) + + @property + def stderr(self) -> str: + """The standard error as unicode string. + + .. versionchanged:: 8.2 + No longer raise an exception, always returns the `` string. + """ + return self.stderr_bytes.decode(self.runner.charset, "replace").replace( + "\r\n", "\n" + ) + + def __repr__(self) -> str: + exc_str = repr(self.exception) if self.exception else "okay" + return f"<{type(self).__name__} {exc_str}>" + + +class CliRunner: + """The CLI runner provides functionality to invoke a Click command line + script for unittesting purposes in a isolated environment. This only + works in single-threaded systems without any concurrency as it changes the + global interpreter state. + + :param charset: the character set for the input and output data. + :param env: a dictionary with environment variables for overriding. + :param echo_stdin: if this is set to `True`, then reading from `` writes + to ``. This is useful for showing examples in + some circumstances. Note that regular prompts + will automatically echo the input. + :param catch_exceptions: Whether to catch any exceptions other than + ``SystemExit`` when running :meth:`~CliRunner.invoke`. + + .. versionchanged:: 8.2 + Added the ``catch_exceptions`` parameter. + + .. versionchanged:: 8.2 + ``mix_stderr`` parameter has been removed. + """ + + def __init__( + self, + charset: str = "utf-8", + env: cabc.Mapping[str, str | None] | None = None, + echo_stdin: bool = False, + catch_exceptions: bool = True, + ) -> None: + self.charset = charset + self.env: cabc.Mapping[str, str | None] = env or {} + self.echo_stdin = echo_stdin + self.catch_exceptions = catch_exceptions + + def get_default_prog_name(self, cli: Command) -> str: + """Given a command object it will return the default program name + for it. The default is the `name` attribute or ``"root"`` if not + set. + """ + return cli.name or "root" + + def make_env( + self, overrides: cabc.Mapping[str, str | None] | None = None + ) -> cabc.Mapping[str, str | None]: + """Returns the environment overrides for invoking a script.""" + rv = dict(self.env) + if overrides: + rv.update(overrides) + return rv + + @contextlib.contextmanager + def isolation( + self, + input: str | bytes | t.IO[t.Any] | None = None, + env: cabc.Mapping[str, str | None] | None = None, + color: bool = False, + ) -> cabc.Iterator[tuple[io.BytesIO, io.BytesIO, io.BytesIO]]: + """A context manager that sets up the isolation for invoking of a + command line tool. This sets up `` with the given input data + and `os.environ` with the overrides from the given dictionary. + This also rebinds some internals in Click to be mocked (like the + prompt functionality). + + This is automatically done in the :meth:`invoke` method. + + :param input: the input stream to put into `sys.stdin`. + :param env: the environment overrides as dictionary. + :param color: whether the output should contain color codes. The + application can still override this explicitly. + + .. versionadded:: 8.2 + An additional output stream is returned, which is a mix of + `` and `` streams. + + .. versionchanged:: 8.2 + Always returns the `` stream. + + .. versionchanged:: 8.0 + `` is opened with ``errors="backslashreplace"`` + instead of the default ``"strict"``. + + .. versionchanged:: 4.0 + Added the ``color`` parameter. + """ + bytes_input = make_input_stream(input, self.charset) + echo_input = None + + old_stdin = sys.stdin + old_stdout = sys.stdout + old_stderr = sys.stderr + old_forced_width = formatting.FORCED_WIDTH + formatting.FORCED_WIDTH = 80 + + env = self.make_env(env) + + stream_mixer = StreamMixer() + + if self.echo_stdin: + bytes_input = echo_input = t.cast( + t.BinaryIO, EchoingStdin(bytes_input, stream_mixer.stdout) + ) + + sys.stdin = text_input = _NamedTextIOWrapper( + bytes_input, encoding=self.charset, name="", mode="r" + ) + + if self.echo_stdin: + # Force unbuffered reads, otherwise TextIOWrapper reads a + # large chunk which is echoed early. + text_input._CHUNK_SIZE = 1 # type: ignore + + sys.stdout = _NamedTextIOWrapper( + stream_mixer.stdout, encoding=self.charset, name="", mode="w" + ) + + sys.stderr = _NamedTextIOWrapper( + stream_mixer.stderr, + encoding=self.charset, + name="", + mode="w", + errors="backslashreplace", + ) + + @_pause_echo(echo_input) # type: ignore + def visible_input(prompt: str | None = None) -> str: + sys.stdout.write(prompt or "") + val = next(text_input).rstrip("\r\n") + sys.stdout.write(f"{val}\n") + sys.stdout.flush() + return val + + @_pause_echo(echo_input) # type: ignore + def hidden_input(prompt: str | None = None) -> str: + sys.stdout.write(f"{prompt or ''}\n") + sys.stdout.flush() + return next(text_input).rstrip("\r\n") + + @_pause_echo(echo_input) # type: ignore + def _getchar(echo: bool) -> str: + char = sys.stdin.read(1) + + if echo: + sys.stdout.write(char) + + sys.stdout.flush() + return char + + default_color = color + + def should_strip_ansi( + stream: t.IO[t.Any] | None = None, color: bool | None = None + ) -> bool: + if color is None: + return not default_color + return not color + + old_visible_prompt_func = termui.visible_prompt_func + old_hidden_prompt_func = termui.hidden_prompt_func + old__getchar_func = termui._getchar + old_should_strip_ansi = utils.should_strip_ansi # type: ignore + old__compat_should_strip_ansi = _compat.should_strip_ansi + termui.visible_prompt_func = visible_input + termui.hidden_prompt_func = hidden_input + termui._getchar = _getchar + utils.should_strip_ansi = should_strip_ansi # type: ignore + _compat.should_strip_ansi = should_strip_ansi + + old_env = {} + try: + for key, value in env.items(): + old_env[key] = os.environ.get(key) + if value is None: + try: + del os.environ[key] + except Exception: + pass + else: + os.environ[key] = value + yield (stream_mixer.stdout, stream_mixer.stderr, stream_mixer.output) + finally: + for key, value in old_env.items(): + if value is None: + try: + del os.environ[key] + except Exception: + pass + else: + os.environ[key] = value + sys.stdout = old_stdout + sys.stderr = old_stderr + sys.stdin = old_stdin + termui.visible_prompt_func = old_visible_prompt_func + termui.hidden_prompt_func = old_hidden_prompt_func + termui._getchar = old__getchar_func + utils.should_strip_ansi = old_should_strip_ansi # type: ignore + _compat.should_strip_ansi = old__compat_should_strip_ansi + formatting.FORCED_WIDTH = old_forced_width + + def invoke( + self, + cli: Command, + args: str | cabc.Sequence[str] | None = None, + input: str | bytes | t.IO[t.Any] | None = None, + env: cabc.Mapping[str, str | None] | None = None, + catch_exceptions: bool | None = None, + color: bool = False, + **extra: t.Any, + ) -> Result: + """Invokes a command in an isolated environment. The arguments are + forwarded directly to the command line script, the `extra` keyword + arguments are passed to the :meth:`~clickpkg.Command.main` function of + the command. + + This returns a :class:`Result` object. + + :param cli: the command to invoke + :param args: the arguments to invoke. It may be given as an iterable + or a string. When given as string it will be interpreted + as a Unix shell command. More details at + :func:`shlex.split`. + :param input: the input data for `sys.stdin`. + :param env: the environment overrides. + :param catch_exceptions: Whether to catch any other exceptions than + ``SystemExit``. If :data:`None`, the value + from :class:`CliRunner` is used. + :param extra: the keyword arguments to pass to :meth:`main`. + :param color: whether the output should contain color codes. The + application can still override this explicitly. + + .. versionadded:: 8.2 + The result object has the ``output_bytes`` attribute with + the mix of ``stdout_bytes`` and ``stderr_bytes``, as the user would + see it in its terminal. + + .. versionchanged:: 8.2 + The result object always returns the ``stderr_bytes`` stream. + + .. versionchanged:: 8.0 + The result object has the ``return_value`` attribute with + the value returned from the invoked command. + + .. versionchanged:: 4.0 + Added the ``color`` parameter. + + .. versionchanged:: 3.0 + Added the ``catch_exceptions`` parameter. + + .. versionchanged:: 3.0 + The result object has the ``exc_info`` attribute with the + traceback if available. + """ + exc_info = None + if catch_exceptions is None: + catch_exceptions = self.catch_exceptions + + with self.isolation(input=input, env=env, color=color) as outstreams: + return_value = None + exception: BaseException | None = None + exit_code = 0 + + if isinstance(args, str): + args = shlex.split(args) + + try: + prog_name = extra.pop("prog_name") + except KeyError: + prog_name = self.get_default_prog_name(cli) + + try: + return_value = cli.main(args=args or (), prog_name=prog_name, **extra) + except SystemExit as e: + exc_info = sys.exc_info() + e_code = t.cast("int | t.Any | None", e.code) + + if e_code is None: + e_code = 0 + + if e_code != 0: + exception = e + + if not isinstance(e_code, int): + sys.stdout.write(str(e_code)) + sys.stdout.write("\n") + e_code = 1 + + exit_code = e_code + + except Exception as e: + if not catch_exceptions: + raise + exception = e + exit_code = 1 + exc_info = sys.exc_info() + finally: + sys.stdout.flush() + sys.stderr.flush() + stdout = outstreams[0].getvalue() + stderr = outstreams[1].getvalue() + output = outstreams[2].getvalue() + + return Result( + runner=self, + stdout_bytes=stdout, + stderr_bytes=stderr, + output_bytes=output, + return_value=return_value, + exit_code=exit_code, + exception=exception, + exc_info=exc_info, # type: ignore + ) + + @contextlib.contextmanager + def isolated_filesystem( + self, temp_dir: str | os.PathLike[str] | None = None + ) -> cabc.Iterator[str]: + """A context manager that creates a temporary directory and + changes the current working directory to it. This isolates tests + that affect the contents of the CWD to prevent them from + interfering with each other. + + :param temp_dir: Create the temporary directory under this + directory. If given, the created directory is not removed + when exiting. + + .. versionchanged:: 8.0 + Added the ``temp_dir`` parameter. + """ + cwd = os.getcwd() + dt = tempfile.mkdtemp(dir=temp_dir) + os.chdir(dt) + + try: + yield dt + finally: + os.chdir(cwd) + + if temp_dir is None: + try: + shutil.rmtree(dt) + except OSError: + pass diff --git a/venv/lib/python3.12/site-packages/click/types.py b/venv/lib/python3.12/site-packages/click/types.py new file mode 100644 index 00000000..684cb3b1 --- /dev/null +++ b/venv/lib/python3.12/site-packages/click/types.py @@ -0,0 +1,1165 @@ +from __future__ import annotations + +import collections.abc as cabc +import enum +import os +import stat +import sys +import typing as t +from datetime import datetime +from gettext import gettext as _ +from gettext import ngettext + +from ._compat import _get_argv_encoding +from ._compat import open_stream +from .exceptions import BadParameter +from .utils import format_filename +from .utils import LazyFile +from .utils import safecall + +if t.TYPE_CHECKING: + import typing_extensions as te + + from .core import Context + from .core import Parameter + from .shell_completion import CompletionItem + +ParamTypeValue = t.TypeVar("ParamTypeValue") + + +class ParamType: + """Represents the type of a parameter. Validates and converts values + from the command line or Python into the correct type. + + To implement a custom type, subclass and implement at least the + following: + + - The :attr:`name` class attribute must be set. + - Calling an instance of the type with ``None`` must return + ``None``. This is already implemented by default. + - :meth:`convert` must convert string values to the correct type. + - :meth:`convert` must accept values that are already the correct + type. + - It must be able to convert a value if the ``ctx`` and ``param`` + arguments are ``None``. This can occur when converting prompt + input. + """ + + is_composite: t.ClassVar[bool] = False + arity: t.ClassVar[int] = 1 + + #: the descriptive name of this type + name: str + + #: if a list of this type is expected and the value is pulled from a + #: string environment variable, this is what splits it up. `None` + #: means any whitespace. For all parameters the general rule is that + #: whitespace splits them up. The exception are paths and files which + #: are split by ``os.path.pathsep`` by default (":" on Unix and ";" on + #: Windows). + envvar_list_splitter: t.ClassVar[str | None] = None + + def to_info_dict(self) -> dict[str, t.Any]: + """Gather information that could be useful for a tool generating + user-facing documentation. + + Use :meth:`click.Context.to_info_dict` to traverse the entire + CLI structure. + + .. versionadded:: 8.0 + """ + # The class name without the "ParamType" suffix. + param_type = type(self).__name__.partition("ParamType")[0] + param_type = param_type.partition("ParameterType")[0] + + # Custom subclasses might not remember to set a name. + if hasattr(self, "name"): + name = self.name + else: + name = param_type + + return {"param_type": param_type, "name": name} + + def __call__( + self, + value: t.Any, + param: Parameter | None = None, + ctx: Context | None = None, + ) -> t.Any: + if value is not None: + return self.convert(value, param, ctx) + + def get_metavar(self, param: Parameter, ctx: Context) -> str | None: + """Returns the metavar default for this param if it provides one.""" + + def get_missing_message(self, param: Parameter, ctx: Context | None) -> str | None: + """Optionally might return extra information about a missing + parameter. + + .. versionadded:: 2.0 + """ + + def convert( + self, value: t.Any, param: Parameter | None, ctx: Context | None + ) -> t.Any: + """Convert the value to the correct type. This is not called if + the value is ``None`` (the missing value). + + This must accept string values from the command line, as well as + values that are already the correct type. It may also convert + other compatible types. + + The ``param`` and ``ctx`` arguments may be ``None`` in certain + situations, such as when converting prompt input. + + If the value cannot be converted, call :meth:`fail` with a + descriptive message. + + :param value: The value to convert. + :param param: The parameter that is using this type to convert + its value. May be ``None``. + :param ctx: The current context that arrived at this value. May + be ``None``. + """ + return value + + def split_envvar_value(self, rv: str) -> cabc.Sequence[str]: + """Given a value from an environment variable this splits it up + into small chunks depending on the defined envvar list splitter. + + If the splitter is set to `None`, which means that whitespace splits, + then leading and trailing whitespace is ignored. Otherwise, leading + and trailing splitters usually lead to empty items being included. + """ + return (rv or "").split(self.envvar_list_splitter) + + def fail( + self, + message: str, + param: Parameter | None = None, + ctx: Context | None = None, + ) -> t.NoReturn: + """Helper method to fail with an invalid value message.""" + raise BadParameter(message, ctx=ctx, param=param) + + def shell_complete( + self, ctx: Context, param: Parameter, incomplete: str + ) -> list[CompletionItem]: + """Return a list of + :class:`~click.shell_completion.CompletionItem` objects for the + incomplete value. Most types do not provide completions, but + some do, and this allows custom types to provide custom + completions as well. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + return [] + + +class CompositeParamType(ParamType): + is_composite = True + + @property + def arity(self) -> int: # type: ignore + raise NotImplementedError() + + +class FuncParamType(ParamType): + def __init__(self, func: t.Callable[[t.Any], t.Any]) -> None: + self.name: str = func.__name__ + self.func = func + + def to_info_dict(self) -> dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict["func"] = self.func + return info_dict + + def convert( + self, value: t.Any, param: Parameter | None, ctx: Context | None + ) -> t.Any: + try: + return self.func(value) + except ValueError: + try: + value = str(value) + except UnicodeError: + value = value.decode("utf-8", "replace") + + self.fail(value, param, ctx) + + +class UnprocessedParamType(ParamType): + name = "text" + + def convert( + self, value: t.Any, param: Parameter | None, ctx: Context | None + ) -> t.Any: + return value + + def __repr__(self) -> str: + return "UNPROCESSED" + + +class StringParamType(ParamType): + name = "text" + + def convert( + self, value: t.Any, param: Parameter | None, ctx: Context | None + ) -> t.Any: + if isinstance(value, bytes): + enc = _get_argv_encoding() + try: + value = value.decode(enc) + except UnicodeError: + fs_enc = sys.getfilesystemencoding() + if fs_enc != enc: + try: + value = value.decode(fs_enc) + except UnicodeError: + value = value.decode("utf-8", "replace") + else: + value = value.decode("utf-8", "replace") + return value + return str(value) + + def __repr__(self) -> str: + return "STRING" + + +class Choice(ParamType, t.Generic[ParamTypeValue]): + """The choice type allows a value to be checked against a fixed set + of supported values. + + You may pass any iterable value which will be converted to a tuple + and thus will only be iterated once. + + The resulting value will always be one of the originally passed choices. + See :meth:`normalize_choice` for more info on the mapping of strings + to choices. See :ref:`choice-opts` for an example. + + :param case_sensitive: Set to false to make choices case + insensitive. Defaults to true. + + .. versionchanged:: 8.2.0 + Non-``str`` ``choices`` are now supported. It can additionally be any + iterable. Before you were not recommended to pass anything but a list or + tuple. + + .. versionadded:: 8.2.0 + Choice normalization can be overridden via :meth:`normalize_choice`. + """ + + name = "choice" + + def __init__( + self, choices: cabc.Iterable[ParamTypeValue], case_sensitive: bool = True + ) -> None: + self.choices: cabc.Sequence[ParamTypeValue] = tuple(choices) + self.case_sensitive = case_sensitive + + def to_info_dict(self) -> dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict["choices"] = self.choices + info_dict["case_sensitive"] = self.case_sensitive + return info_dict + + def _normalized_mapping( + self, ctx: Context | None = None + ) -> cabc.Mapping[ParamTypeValue, str]: + """ + Returns mapping where keys are the original choices and the values are + the normalized values that are accepted via the command line. + + This is a simple wrapper around :meth:`normalize_choice`, use that + instead which is supported. + """ + return { + choice: self.normalize_choice( + choice=choice, + ctx=ctx, + ) + for choice in self.choices + } + + def normalize_choice(self, choice: ParamTypeValue, ctx: Context | None) -> str: + """ + Normalize a choice value, used to map a passed string to a choice. + Each choice must have a unique normalized value. + + By default uses :meth:`Context.token_normalize_func` and if not case + sensitive, convert it to a casefolded value. + + .. versionadded:: 8.2.0 + """ + normed_value = choice.name if isinstance(choice, enum.Enum) else str(choice) + + if ctx is not None and ctx.token_normalize_func is not None: + normed_value = ctx.token_normalize_func(normed_value) + + if not self.case_sensitive: + normed_value = normed_value.casefold() + + return normed_value + + def get_metavar(self, param: Parameter, ctx: Context) -> str | None: + if param.param_type_name == "option" and not param.show_choices: # type: ignore + choice_metavars = [ + convert_type(type(choice)).name.upper() for choice in self.choices + ] + choices_str = "|".join([*dict.fromkeys(choice_metavars)]) + else: + choices_str = "|".join( + [str(i) for i in self._normalized_mapping(ctx=ctx).values()] + ) + + # Use curly braces to indicate a required argument. + if param.required and param.param_type_name == "argument": + return f"{{{choices_str}}}" + + # Use square braces to indicate an option or optional argument. + return f"[{choices_str}]" + + def get_missing_message(self, param: Parameter, ctx: Context | None) -> str: + """ + Message shown when no choice is passed. + + .. versionchanged:: 8.2.0 Added ``ctx`` argument. + """ + return _("Choose from:\n\t{choices}").format( + choices=",\n\t".join(self._normalized_mapping(ctx=ctx).values()) + ) + + def convert( + self, value: t.Any, param: Parameter | None, ctx: Context | None + ) -> ParamTypeValue: + """ + For a given value from the parser, normalize it and find its + matching normalized value in the list of choices. Then return the + matched "original" choice. + """ + normed_value = self.normalize_choice(choice=value, ctx=ctx) + normalized_mapping = self._normalized_mapping(ctx=ctx) + + try: + return next( + original + for original, normalized in normalized_mapping.items() + if normalized == normed_value + ) + except StopIteration: + self.fail( + self.get_invalid_choice_message(value=value, ctx=ctx), + param=param, + ctx=ctx, + ) + + def get_invalid_choice_message(self, value: t.Any, ctx: Context | None) -> str: + """Get the error message when the given choice is invalid. + + :param value: The invalid value. + + .. versionadded:: 8.2 + """ + choices_str = ", ".join(map(repr, self._normalized_mapping(ctx=ctx).values())) + return ngettext( + "{value!r} is not {choice}.", + "{value!r} is not one of {choices}.", + len(self.choices), + ).format(value=value, choice=choices_str, choices=choices_str) + + def __repr__(self) -> str: + return f"Choice({list(self.choices)})" + + def shell_complete( + self, ctx: Context, param: Parameter, incomplete: str + ) -> list[CompletionItem]: + """Complete choices that start with the incomplete value. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + str_choices = map(str, self.choices) + + if self.case_sensitive: + matched = (c for c in str_choices if c.startswith(incomplete)) + else: + incomplete = incomplete.lower() + matched = (c for c in str_choices if c.lower().startswith(incomplete)) + + return [CompletionItem(c) for c in matched] + + +class DateTime(ParamType): + """The DateTime type converts date strings into `datetime` objects. + + The format strings which are checked are configurable, but default to some + common (non-timezone aware) ISO 8601 formats. + + When specifying *DateTime* formats, you should only pass a list or a tuple. + Other iterables, like generators, may lead to surprising results. + + The format strings are processed using ``datetime.strptime``, and this + consequently defines the format strings which are allowed. + + Parsing is tried using each format, in order, and the first format which + parses successfully is used. + + :param formats: A list or tuple of date format strings, in the order in + which they should be tried. Defaults to + ``'%Y-%m-%d'``, ``'%Y-%m-%dT%H:%M:%S'``, + ``'%Y-%m-%d %H:%M:%S'``. + """ + + name = "datetime" + + def __init__(self, formats: cabc.Sequence[str] | None = None): + self.formats: cabc.Sequence[str] = formats or [ + "%Y-%m-%d", + "%Y-%m-%dT%H:%M:%S", + "%Y-%m-%d %H:%M:%S", + ] + + def to_info_dict(self) -> dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict["formats"] = self.formats + return info_dict + + def get_metavar(self, param: Parameter, ctx: Context) -> str | None: + return f"[{'|'.join(self.formats)}]" + + def _try_to_convert_date(self, value: t.Any, format: str) -> datetime | None: + try: + return datetime.strptime(value, format) + except ValueError: + return None + + def convert( + self, value: t.Any, param: Parameter | None, ctx: Context | None + ) -> t.Any: + if isinstance(value, datetime): + return value + + for format in self.formats: + converted = self._try_to_convert_date(value, format) + + if converted is not None: + return converted + + formats_str = ", ".join(map(repr, self.formats)) + self.fail( + ngettext( + "{value!r} does not match the format {format}.", + "{value!r} does not match the formats {formats}.", + len(self.formats), + ).format(value=value, format=formats_str, formats=formats_str), + param, + ctx, + ) + + def __repr__(self) -> str: + return "DateTime" + + +class _NumberParamTypeBase(ParamType): + _number_class: t.ClassVar[type[t.Any]] + + def convert( + self, value: t.Any, param: Parameter | None, ctx: Context | None + ) -> t.Any: + try: + return self._number_class(value) + except ValueError: + self.fail( + _("{value!r} is not a valid {number_type}.").format( + value=value, number_type=self.name + ), + param, + ctx, + ) + + +class _NumberRangeBase(_NumberParamTypeBase): + def __init__( + self, + min: float | None = None, + max: float | None = None, + min_open: bool = False, + max_open: bool = False, + clamp: bool = False, + ) -> None: + self.min = min + self.max = max + self.min_open = min_open + self.max_open = max_open + self.clamp = clamp + + def to_info_dict(self) -> dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict.update( + min=self.min, + max=self.max, + min_open=self.min_open, + max_open=self.max_open, + clamp=self.clamp, + ) + return info_dict + + def convert( + self, value: t.Any, param: Parameter | None, ctx: Context | None + ) -> t.Any: + import operator + + rv = super().convert(value, param, ctx) + lt_min: bool = self.min is not None and ( + operator.le if self.min_open else operator.lt + )(rv, self.min) + gt_max: bool = self.max is not None and ( + operator.ge if self.max_open else operator.gt + )(rv, self.max) + + if self.clamp: + if lt_min: + return self._clamp(self.min, 1, self.min_open) # type: ignore + + if gt_max: + return self._clamp(self.max, -1, self.max_open) # type: ignore + + if lt_min or gt_max: + self.fail( + _("{value} is not in the range {range}.").format( + value=rv, range=self._describe_range() + ), + param, + ctx, + ) + + return rv + + def _clamp(self, bound: float, dir: t.Literal[1, -1], open: bool) -> float: + """Find the valid value to clamp to bound in the given + direction. + + :param bound: The boundary value. + :param dir: 1 or -1 indicating the direction to move. + :param open: If true, the range does not include the bound. + """ + raise NotImplementedError + + def _describe_range(self) -> str: + """Describe the range for use in help text.""" + if self.min is None: + op = "<" if self.max_open else "<=" + return f"x{op}{self.max}" + + if self.max is None: + op = ">" if self.min_open else ">=" + return f"x{op}{self.min}" + + lop = "<" if self.min_open else "<=" + rop = "<" if self.max_open else "<=" + return f"{self.min}{lop}x{rop}{self.max}" + + def __repr__(self) -> str: + clamp = " clamped" if self.clamp else "" + return f"<{type(self).__name__} {self._describe_range()}{clamp}>" + + +class IntParamType(_NumberParamTypeBase): + name = "integer" + _number_class = int + + def __repr__(self) -> str: + return "INT" + + +class IntRange(_NumberRangeBase, IntParamType): + """Restrict an :data:`click.INT` value to a range of accepted + values. See :ref:`ranges`. + + If ``min`` or ``max`` are not passed, any value is accepted in that + direction. If ``min_open`` or ``max_open`` are enabled, the + corresponding boundary is not included in the range. + + If ``clamp`` is enabled, a value outside the range is clamped to the + boundary instead of failing. + + .. versionchanged:: 8.0 + Added the ``min_open`` and ``max_open`` parameters. + """ + + name = "integer range" + + def _clamp( # type: ignore + self, bound: int, dir: t.Literal[1, -1], open: bool + ) -> int: + if not open: + return bound + + return bound + dir + + +class FloatParamType(_NumberParamTypeBase): + name = "float" + _number_class = float + + def __repr__(self) -> str: + return "FLOAT" + + +class FloatRange(_NumberRangeBase, FloatParamType): + """Restrict a :data:`click.FLOAT` value to a range of accepted + values. See :ref:`ranges`. + + If ``min`` or ``max`` are not passed, any value is accepted in that + direction. If ``min_open`` or ``max_open`` are enabled, the + corresponding boundary is not included in the range. + + If ``clamp`` is enabled, a value outside the range is clamped to the + boundary instead of failing. This is not supported if either + boundary is marked ``open``. + + .. versionchanged:: 8.0 + Added the ``min_open`` and ``max_open`` parameters. + """ + + name = "float range" + + def __init__( + self, + min: float | None = None, + max: float | None = None, + min_open: bool = False, + max_open: bool = False, + clamp: bool = False, + ) -> None: + super().__init__( + min=min, max=max, min_open=min_open, max_open=max_open, clamp=clamp + ) + + if (min_open or max_open) and clamp: + raise TypeError("Clamping is not supported for open bounds.") + + def _clamp(self, bound: float, dir: t.Literal[1, -1], open: bool) -> float: + if not open: + return bound + + # Could use math.nextafter here, but clamping an + # open float range doesn't seem to be particularly useful. It's + # left up to the user to write a callback to do it if needed. + raise RuntimeError("Clamping is not supported for open bounds.") + + +class BoolParamType(ParamType): + name = "boolean" + + def convert( + self, value: t.Any, param: Parameter | None, ctx: Context | None + ) -> t.Any: + if value in {False, True}: + return bool(value) + + norm = value.strip().lower() + + if norm in {"1", "true", "t", "yes", "y", "on"}: + return True + + if norm in {"0", "false", "f", "no", "n", "off"}: + return False + + self.fail( + _("{value!r} is not a valid boolean.").format(value=value), param, ctx + ) + + def __repr__(self) -> str: + return "BOOL" + + +class UUIDParameterType(ParamType): + name = "uuid" + + def convert( + self, value: t.Any, param: Parameter | None, ctx: Context | None + ) -> t.Any: + import uuid + + if isinstance(value, uuid.UUID): + return value + + value = value.strip() + + try: + return uuid.UUID(value) + except ValueError: + self.fail( + _("{value!r} is not a valid UUID.").format(value=value), param, ctx + ) + + def __repr__(self) -> str: + return "UUID" + + +class File(ParamType): + """Declares a parameter to be a file for reading or writing. The file + is automatically closed once the context tears down (after the command + finished working). + + Files can be opened for reading or writing. The special value ``-`` + indicates stdin or stdout depending on the mode. + + By default, the file is opened for reading text data, but it can also be + opened in binary mode or for writing. The encoding parameter can be used + to force a specific encoding. + + The `lazy` flag controls if the file should be opened immediately or upon + first IO. The default is to be non-lazy for standard input and output + streams as well as files opened for reading, `lazy` otherwise. When opening a + file lazily for reading, it is still opened temporarily for validation, but + will not be held open until first IO. lazy is mainly useful when opening + for writing to avoid creating the file until it is needed. + + Files can also be opened atomically in which case all writes go into a + separate file in the same folder and upon completion the file will + be moved over to the original location. This is useful if a file + regularly read by other users is modified. + + See :ref:`file-args` for more information. + + .. versionchanged:: 2.0 + Added the ``atomic`` parameter. + """ + + name = "filename" + envvar_list_splitter: t.ClassVar[str] = os.path.pathsep + + def __init__( + self, + mode: str = "r", + encoding: str | None = None, + errors: str | None = "strict", + lazy: bool | None = None, + atomic: bool = False, + ) -> None: + self.mode = mode + self.encoding = encoding + self.errors = errors + self.lazy = lazy + self.atomic = atomic + + def to_info_dict(self) -> dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict.update(mode=self.mode, encoding=self.encoding) + return info_dict + + def resolve_lazy_flag(self, value: str | os.PathLike[str]) -> bool: + if self.lazy is not None: + return self.lazy + if os.fspath(value) == "-": + return False + elif "w" in self.mode: + return True + return False + + def convert( + self, + value: str | os.PathLike[str] | t.IO[t.Any], + param: Parameter | None, + ctx: Context | None, + ) -> t.IO[t.Any]: + if _is_file_like(value): + return value + + value = t.cast("str | os.PathLike[str]", value) + + try: + lazy = self.resolve_lazy_flag(value) + + if lazy: + lf = LazyFile( + value, self.mode, self.encoding, self.errors, atomic=self.atomic + ) + + if ctx is not None: + ctx.call_on_close(lf.close_intelligently) + + return t.cast("t.IO[t.Any]", lf) + + f, should_close = open_stream( + value, self.mode, self.encoding, self.errors, atomic=self.atomic + ) + + # If a context is provided, we automatically close the file + # at the end of the context execution (or flush out). If a + # context does not exist, it's the caller's responsibility to + # properly close the file. This for instance happens when the + # type is used with prompts. + if ctx is not None: + if should_close: + ctx.call_on_close(safecall(f.close)) + else: + ctx.call_on_close(safecall(f.flush)) + + return f + except OSError as e: + self.fail(f"'{format_filename(value)}': {e.strerror}", param, ctx) + + def shell_complete( + self, ctx: Context, param: Parameter, incomplete: str + ) -> list[CompletionItem]: + """Return a special completion marker that tells the completion + system to use the shell to provide file path completions. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + return [CompletionItem(incomplete, type="file")] + + +def _is_file_like(value: t.Any) -> te.TypeGuard[t.IO[t.Any]]: + return hasattr(value, "read") or hasattr(value, "write") + + +class Path(ParamType): + """The ``Path`` type is similar to the :class:`File` type, but + returns the filename instead of an open file. Various checks can be + enabled to validate the type of file and permissions. + + :param exists: The file or directory needs to exist for the value to + be valid. If this is not set to ``True``, and the file does not + exist, then all further checks are silently skipped. + :param file_okay: Allow a file as a value. + :param dir_okay: Allow a directory as a value. + :param readable: if true, a readable check is performed. + :param writable: if true, a writable check is performed. + :param executable: if true, an executable check is performed. + :param resolve_path: Make the value absolute and resolve any + symlinks. A ``~`` is not expanded, as this is supposed to be + done by the shell only. + :param allow_dash: Allow a single dash as a value, which indicates + a standard stream (but does not open it). Use + :func:`~click.open_file` to handle opening this value. + :param path_type: Convert the incoming path value to this type. If + ``None``, keep Python's default, which is ``str``. Useful to + convert to :class:`pathlib.Path`. + + .. versionchanged:: 8.1 + Added the ``executable`` parameter. + + .. versionchanged:: 8.0 + Allow passing ``path_type=pathlib.Path``. + + .. versionchanged:: 6.0 + Added the ``allow_dash`` parameter. + """ + + envvar_list_splitter: t.ClassVar[str] = os.path.pathsep + + def __init__( + self, + exists: bool = False, + file_okay: bool = True, + dir_okay: bool = True, + writable: bool = False, + readable: bool = True, + resolve_path: bool = False, + allow_dash: bool = False, + path_type: type[t.Any] | None = None, + executable: bool = False, + ): + self.exists = exists + self.file_okay = file_okay + self.dir_okay = dir_okay + self.readable = readable + self.writable = writable + self.executable = executable + self.resolve_path = resolve_path + self.allow_dash = allow_dash + self.type = path_type + + if self.file_okay and not self.dir_okay: + self.name: str = _("file") + elif self.dir_okay and not self.file_okay: + self.name = _("directory") + else: + self.name = _("path") + + def to_info_dict(self) -> dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict.update( + exists=self.exists, + file_okay=self.file_okay, + dir_okay=self.dir_okay, + writable=self.writable, + readable=self.readable, + allow_dash=self.allow_dash, + ) + return info_dict + + def coerce_path_result( + self, value: str | os.PathLike[str] + ) -> str | bytes | os.PathLike[str]: + if self.type is not None and not isinstance(value, self.type): + if self.type is str: + return os.fsdecode(value) + elif self.type is bytes: + return os.fsencode(value) + else: + return t.cast("os.PathLike[str]", self.type(value)) + + return value + + def convert( + self, + value: str | os.PathLike[str], + param: Parameter | None, + ctx: Context | None, + ) -> str | bytes | os.PathLike[str]: + rv = value + + is_dash = self.file_okay and self.allow_dash and rv in (b"-", "-") + + if not is_dash: + if self.resolve_path: + rv = os.path.realpath(rv) + + try: + st = os.stat(rv) + except OSError: + if not self.exists: + return self.coerce_path_result(rv) + self.fail( + _("{name} {filename!r} does not exist.").format( + name=self.name.title(), filename=format_filename(value) + ), + param, + ctx, + ) + + if not self.file_okay and stat.S_ISREG(st.st_mode): + self.fail( + _("{name} {filename!r} is a file.").format( + name=self.name.title(), filename=format_filename(value) + ), + param, + ctx, + ) + if not self.dir_okay and stat.S_ISDIR(st.st_mode): + self.fail( + _("{name} {filename!r} is a directory.").format( + name=self.name.title(), filename=format_filename(value) + ), + param, + ctx, + ) + + if self.readable and not os.access(rv, os.R_OK): + self.fail( + _("{name} {filename!r} is not readable.").format( + name=self.name.title(), filename=format_filename(value) + ), + param, + ctx, + ) + + if self.writable and not os.access(rv, os.W_OK): + self.fail( + _("{name} {filename!r} is not writable.").format( + name=self.name.title(), filename=format_filename(value) + ), + param, + ctx, + ) + + if self.executable and not os.access(value, os.X_OK): + self.fail( + _("{name} {filename!r} is not executable.").format( + name=self.name.title(), filename=format_filename(value) + ), + param, + ctx, + ) + + return self.coerce_path_result(rv) + + def shell_complete( + self, ctx: Context, param: Parameter, incomplete: str + ) -> list[CompletionItem]: + """Return a special completion marker that tells the completion + system to use the shell to provide path completions for only + directories or any paths. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + type = "dir" if self.dir_okay and not self.file_okay else "file" + return [CompletionItem(incomplete, type=type)] + + +class Tuple(CompositeParamType): + """The default behavior of Click is to apply a type on a value directly. + This works well in most cases, except for when `nargs` is set to a fixed + count and different types should be used for different items. In this + case the :class:`Tuple` type can be used. This type can only be used + if `nargs` is set to a fixed number. + + For more information see :ref:`tuple-type`. + + This can be selected by using a Python tuple literal as a type. + + :param types: a list of types that should be used for the tuple items. + """ + + def __init__(self, types: cabc.Sequence[type[t.Any] | ParamType]) -> None: + self.types: cabc.Sequence[ParamType] = [convert_type(ty) for ty in types] + + def to_info_dict(self) -> dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict["types"] = [t.to_info_dict() for t in self.types] + return info_dict + + @property + def name(self) -> str: # type: ignore + return f"<{' '.join(ty.name for ty in self.types)}>" + + @property + def arity(self) -> int: # type: ignore + return len(self.types) + + def convert( + self, value: t.Any, param: Parameter | None, ctx: Context | None + ) -> t.Any: + len_type = len(self.types) + len_value = len(value) + + if len_value != len_type: + self.fail( + ngettext( + "{len_type} values are required, but {len_value} was given.", + "{len_type} values are required, but {len_value} were given.", + len_value, + ).format(len_type=len_type, len_value=len_value), + param=param, + ctx=ctx, + ) + + return tuple( + ty(x, param, ctx) for ty, x in zip(self.types, value, strict=False) + ) + + +def convert_type(ty: t.Any | None, default: t.Any | None = None) -> ParamType: + """Find the most appropriate :class:`ParamType` for the given Python + type. If the type isn't provided, it can be inferred from a default + value. + """ + guessed_type = False + + if ty is None and default is not None: + if isinstance(default, (tuple, list)): + # If the default is empty, ty will remain None and will + # return STRING. + if default: + item = default[0] + + # A tuple of tuples needs to detect the inner types. + # Can't call convert recursively because that would + # incorrectly unwind the tuple to a single type. + if isinstance(item, (tuple, list)): + ty = tuple(map(type, item)) + else: + ty = type(item) + else: + ty = type(default) + + guessed_type = True + + if isinstance(ty, tuple): + return Tuple(ty) + + if isinstance(ty, ParamType): + return ty + + if ty is str or ty is None: + return STRING + + if ty is int: + return INT + + if ty is float: + return FLOAT + + if ty is bool: + return BOOL + + if guessed_type: + return STRING + + if __debug__: + try: + if issubclass(ty, ParamType): + raise AssertionError( + f"Attempted to use an uninstantiated parameter type ({ty})." + ) + except TypeError: + # ty is an instance (correct), so issubclass fails. + pass + + return FuncParamType(ty) + + +#: A dummy parameter type that just does nothing. From a user's +#: perspective this appears to just be the same as `STRING` but +#: internally no string conversion takes place if the input was bytes. +#: This is usually useful when working with file paths as they can +#: appear in bytes and unicode. +#: +#: For path related uses the :class:`Path` type is a better choice but +#: there are situations where an unprocessed type is useful which is why +#: it is is provided. +#: +#: .. versionadded:: 4.0 +UNPROCESSED = UnprocessedParamType() + +#: A unicode string parameter type which is the implicit default. This +#: can also be selected by using ``str`` as type. +STRING = StringParamType() + +#: An integer parameter. This can also be selected by using ``int`` as +#: type. +INT = IntParamType() + +#: A floating point value parameter. This can also be selected by using +#: ``float`` as type. +FLOAT = FloatParamType() + +#: A boolean parameter. This is the default for boolean flags. This can +#: also be selected by using ``bool`` as a type. +BOOL = BoolParamType() + +#: A UUID parameter. +UUID = UUIDParameterType() + + +class OptionHelpExtra(t.TypedDict, total=False): + envvars: tuple[str, ...] + default: str + range: str + required: str diff --git a/venv/lib/python3.12/site-packages/click/utils.py b/venv/lib/python3.12/site-packages/click/utils.py new file mode 100644 index 00000000..ab2fe588 --- /dev/null +++ b/venv/lib/python3.12/site-packages/click/utils.py @@ -0,0 +1,627 @@ +from __future__ import annotations + +import collections.abc as cabc +import os +import re +import sys +import typing as t +from functools import update_wrapper +from types import ModuleType +from types import TracebackType + +from ._compat import _default_text_stderr +from ._compat import _default_text_stdout +from ._compat import _find_binary_writer +from ._compat import auto_wrap_for_ansi +from ._compat import binary_streams +from ._compat import open_stream +from ._compat import should_strip_ansi +from ._compat import strip_ansi +from ._compat import text_streams +from ._compat import WIN +from .globals import resolve_color_default + +if t.TYPE_CHECKING: + import typing_extensions as te + + P = te.ParamSpec("P") + +R = t.TypeVar("R") + + +def _posixify(name: str) -> str: + return "-".join(name.split()).lower() + + +def safecall(func: t.Callable[P, R]) -> t.Callable[P, R | None]: + """Wraps a function so that it swallows exceptions.""" + + def wrapper(*args: P.args, **kwargs: P.kwargs) -> R | None: + try: + return func(*args, **kwargs) + except Exception: + pass + return None + + return update_wrapper(wrapper, func) + + +def make_str(value: t.Any) -> str: + """Converts a value into a valid string.""" + if isinstance(value, bytes): + try: + return value.decode(sys.getfilesystemencoding()) + except UnicodeError: + return value.decode("utf-8", "replace") + return str(value) + + +def make_default_short_help(help: str, max_length: int = 45) -> str: + """Returns a condensed version of help string.""" + # Consider only the first paragraph. + paragraph_end = help.find("\n\n") + + if paragraph_end != -1: + help = help[:paragraph_end] + + # Collapse newlines, tabs, and spaces. + words = help.split() + + if not words: + return "" + + # The first paragraph started with a "no rewrap" marker, ignore it. + if words[0] == "\b": + words = words[1:] + + total_length = 0 + last_index = len(words) - 1 + + for i, word in enumerate(words): + total_length += len(word) + (i > 0) + + if total_length > max_length: # too long, truncate + break + + if word[-1] == ".": # sentence end, truncate without "..." + return " ".join(words[: i + 1]) + + if total_length == max_length and i != last_index: + break # not at sentence end, truncate with "..." + else: + return " ".join(words) # no truncation needed + + # Account for the length of the suffix. + total_length += len("...") + + # remove words until the length is short enough + while i > 0: + total_length -= len(words[i]) + (i > 0) + + if total_length <= max_length: + break + + i -= 1 + + return " ".join(words[:i]) + "..." + + +class LazyFile: + """A lazy file works like a regular file but it does not fully open + the file but it does perform some basic checks early to see if the + filename parameter does make sense. This is useful for safely opening + files for writing. + """ + + def __init__( + self, + filename: str | os.PathLike[str], + mode: str = "r", + encoding: str | None = None, + errors: str | None = "strict", + atomic: bool = False, + ): + self.name: str = os.fspath(filename) + self.mode = mode + self.encoding = encoding + self.errors = errors + self.atomic = atomic + self._f: t.IO[t.Any] | None + self.should_close: bool + + if self.name == "-": + self._f, self.should_close = open_stream(filename, mode, encoding, errors) + else: + if "r" in mode: + # Open and close the file in case we're opening it for + # reading so that we can catch at least some errors in + # some cases early. + open(filename, mode).close() + self._f = None + self.should_close = True + + def __getattr__(self, name: str) -> t.Any: + return getattr(self.open(), name) + + def __repr__(self) -> str: + if self._f is not None: + return repr(self._f) + return f"" + + def open(self) -> t.IO[t.Any]: + """Opens the file if it's not yet open. This call might fail with + a :exc:`FileError`. Not handling this error will produce an error + that Click shows. + """ + if self._f is not None: + return self._f + try: + rv, self.should_close = open_stream( + self.name, self.mode, self.encoding, self.errors, atomic=self.atomic + ) + except OSError as e: + from .exceptions import FileError + + raise FileError(self.name, hint=e.strerror) from e + self._f = rv + return rv + + def close(self) -> None: + """Closes the underlying file, no matter what.""" + if self._f is not None: + self._f.close() + + def close_intelligently(self) -> None: + """This function only closes the file if it was opened by the lazy + file wrapper. For instance this will never close stdin. + """ + if self.should_close: + self.close() + + def __enter__(self) -> LazyFile: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + tb: TracebackType | None, + ) -> None: + self.close_intelligently() + + def __iter__(self) -> cabc.Iterator[t.AnyStr]: + self.open() + return iter(self._f) # type: ignore + + +class KeepOpenFile: + def __init__(self, file: t.IO[t.Any]) -> None: + self._file: t.IO[t.Any] = file + + def __getattr__(self, name: str) -> t.Any: + return getattr(self._file, name) + + def __enter__(self) -> KeepOpenFile: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + tb: TracebackType | None, + ) -> None: + pass + + def __repr__(self) -> str: + return repr(self._file) + + def __iter__(self) -> cabc.Iterator[t.AnyStr]: + return iter(self._file) + + +def echo( + message: t.Any | None = None, + file: t.IO[t.Any] | None = None, + nl: bool = True, + err: bool = False, + color: bool | None = None, +) -> None: + """Print a message and newline to stdout or a file. This should be + used instead of :func:`print` because it provides better support + for different data, files, and environments. + + Compared to :func:`print`, this does the following: + + - Ensures that the output encoding is not misconfigured on Linux. + - Supports Unicode in the Windows console. + - Supports writing to binary outputs, and supports writing bytes + to text outputs. + - Supports colors and styles on Windows. + - Removes ANSI color and style codes if the output does not look + like an interactive terminal. + - Always flushes the output. + + :param message: The string or bytes to output. Other objects are + converted to strings. + :param file: The file to write to. Defaults to ``stdout``. + :param err: Write to ``stderr`` instead of ``stdout``. + :param nl: Print a newline after the message. Enabled by default. + :param color: Force showing or hiding colors and other styles. By + default Click will remove color if the output does not look like + an interactive terminal. + + .. versionchanged:: 6.0 + Support Unicode output on the Windows console. Click does not + modify ``sys.stdout``, so ``sys.stdout.write()`` and ``print()`` + will still not support Unicode. + + .. versionchanged:: 4.0 + Added the ``color`` parameter. + + .. versionadded:: 3.0 + Added the ``err`` parameter. + + .. versionchanged:: 2.0 + Support colors on Windows if colorama is installed. + """ + if file is None: + if err: + file = _default_text_stderr() + else: + file = _default_text_stdout() + + # There are no standard streams attached to write to. For example, + # pythonw on Windows. + if file is None: + return + + # Convert non bytes/text into the native string type. + if message is not None and not isinstance(message, (str, bytes, bytearray)): + out: str | bytes | None = str(message) + else: + out = message + + if nl: + out = out or "" + if isinstance(out, str): + out += "\n" + else: + out += b"\n" + + if not out: + file.flush() + return + + # If there is a message and the value looks like bytes, we manually + # need to find the binary stream and write the message in there. + # This is done separately so that most stream types will work as you + # would expect. Eg: you can write to StringIO for other cases. + if isinstance(out, (bytes, bytearray)): + binary_file = _find_binary_writer(file) + + if binary_file is not None: + file.flush() + binary_file.write(out) + binary_file.flush() + return + + # ANSI style code support. For no message or bytes, nothing happens. + # When outputting to a file instead of a terminal, strip codes. + else: + color = resolve_color_default(color) + + if should_strip_ansi(file, color): + out = strip_ansi(out) + elif WIN: + if auto_wrap_for_ansi is not None: + file = auto_wrap_for_ansi(file, color) # type: ignore + elif not color: + out = strip_ansi(out) + + file.write(out) # type: ignore + file.flush() + + +def get_binary_stream(name: t.Literal["stdin", "stdout", "stderr"]) -> t.BinaryIO: + """Returns a system stream for byte processing. + + :param name: the name of the stream to open. Valid names are ``'stdin'``, + ``'stdout'`` and ``'stderr'`` + """ + opener = binary_streams.get(name) + if opener is None: + raise TypeError(f"Unknown standard stream '{name}'") + return opener() + + +def get_text_stream( + name: t.Literal["stdin", "stdout", "stderr"], + encoding: str | None = None, + errors: str | None = "strict", +) -> t.TextIO: + """Returns a system stream for text processing. This usually returns + a wrapped stream around a binary stream returned from + :func:`get_binary_stream` but it also can take shortcuts for already + correctly configured streams. + + :param name: the name of the stream to open. Valid names are ``'stdin'``, + ``'stdout'`` and ``'stderr'`` + :param encoding: overrides the detected default encoding. + :param errors: overrides the default error mode. + """ + opener = text_streams.get(name) + if opener is None: + raise TypeError(f"Unknown standard stream '{name}'") + return opener(encoding, errors) + + +def open_file( + filename: str | os.PathLike[str], + mode: str = "r", + encoding: str | None = None, + errors: str | None = "strict", + lazy: bool = False, + atomic: bool = False, +) -> t.IO[t.Any]: + """Open a file, with extra behavior to handle ``'-'`` to indicate + a standard stream, lazy open on write, and atomic write. Similar to + the behavior of the :class:`~click.File` param type. + + If ``'-'`` is given to open ``stdout`` or ``stdin``, the stream is + wrapped so that using it in a context manager will not close it. + This makes it possible to use the function without accidentally + closing a standard stream: + + .. code-block:: python + + with open_file(filename) as f: + ... + + :param filename: The name or Path of the file to open, or ``'-'`` for + ``stdin``/``stdout``. + :param mode: The mode in which to open the file. + :param encoding: The encoding to decode or encode a file opened in + text mode. + :param errors: The error handling mode. + :param lazy: Wait to open the file until it is accessed. For read + mode, the file is temporarily opened to raise access errors + early, then closed until it is read again. + :param atomic: Write to a temporary file and replace the given file + on close. + + .. versionadded:: 3.0 + """ + if lazy: + return t.cast( + "t.IO[t.Any]", LazyFile(filename, mode, encoding, errors, atomic=atomic) + ) + + f, should_close = open_stream(filename, mode, encoding, errors, atomic=atomic) + + if not should_close: + f = t.cast("t.IO[t.Any]", KeepOpenFile(f)) + + return f + + +def format_filename( + filename: str | bytes | os.PathLike[str] | os.PathLike[bytes], + shorten: bool = False, +) -> str: + """Format a filename as a string for display. Ensures the filename can be + displayed by replacing any invalid bytes or surrogate escapes in the name + with the replacement character ``�``. + + Invalid bytes or surrogate escapes will raise an error when written to a + stream with ``errors="strict"``. This will typically happen with ``stdout`` + when the locale is something like ``en_GB.UTF-8``. + + Many scenarios *are* safe to write surrogates though, due to PEP 538 and + PEP 540, including: + + - Writing to ``stderr``, which uses ``errors="backslashreplace"``. + - The system has ``LANG=C.UTF-8``, ``C``, or ``POSIX``. Python opens + stdout and stderr with ``errors="surrogateescape"``. + - None of ``LANG/LC_*`` are set. Python assumes ``LANG=C.UTF-8``. + - Python is started in UTF-8 mode with ``PYTHONUTF8=1`` or ``-X utf8``. + Python opens stdout and stderr with ``errors="surrogateescape"``. + + :param filename: formats a filename for UI display. This will also convert + the filename into unicode without failing. + :param shorten: this optionally shortens the filename to strip of the + path that leads up to it. + """ + if shorten: + filename = os.path.basename(filename) + else: + filename = os.fspath(filename) + + if isinstance(filename, bytes): + filename = filename.decode(sys.getfilesystemencoding(), "replace") + else: + filename = filename.encode("utf-8", "surrogateescape").decode( + "utf-8", "replace" + ) + + return filename + + +def get_app_dir(app_name: str, roaming: bool = True, force_posix: bool = False) -> str: + r"""Returns the config folder for the application. The default behavior + is to return whatever is most appropriate for the operating system. + + To give you an idea, for an app called ``"Foo Bar"``, something like + the following folders could be returned: + + Mac OS X: + ``~/Library/Application Support/Foo Bar`` + Mac OS X (POSIX): + ``~/.foo-bar`` + Unix: + ``~/.config/foo-bar`` + Unix (POSIX): + ``~/.foo-bar`` + Windows (roaming): + ``C:\Users\\AppData\Roaming\Foo Bar`` + Windows (not roaming): + ``C:\Users\\AppData\Local\Foo Bar`` + + .. versionadded:: 2.0 + + :param app_name: the application name. This should be properly capitalized + and can contain whitespace. + :param roaming: controls if the folder should be roaming or not on Windows. + Has no effect otherwise. + :param force_posix: if this is set to `True` then on any POSIX system the + folder will be stored in the home folder with a leading + dot instead of the XDG config home or darwin's + application support folder. + """ + if WIN: + key = "APPDATA" if roaming else "LOCALAPPDATA" + folder = os.environ.get(key) + if folder is None: + folder = os.path.expanduser("~") + return os.path.join(folder, app_name) + if force_posix: + return os.path.join(os.path.expanduser(f"~/.{_posixify(app_name)}")) + if sys.platform == "darwin": + return os.path.join( + os.path.expanduser("~/Library/Application Support"), app_name + ) + return os.path.join( + os.environ.get("XDG_CONFIG_HOME", os.path.expanduser("~/.config")), + _posixify(app_name), + ) + + +class PacifyFlushWrapper: + """This wrapper is used to catch and suppress BrokenPipeErrors resulting + from ``.flush()`` being called on broken pipe during the shutdown/final-GC + of the Python interpreter. Notably ``.flush()`` is always called on + ``sys.stdout`` and ``sys.stderr``. So as to have minimal impact on any + other cleanup code, and the case where the underlying file is not a broken + pipe, all calls and attributes are proxied. + """ + + def __init__(self, wrapped: t.IO[t.Any]) -> None: + self.wrapped = wrapped + + def flush(self) -> None: + try: + self.wrapped.flush() + except OSError as e: + import errno + + if e.errno != errno.EPIPE: + raise + + def __getattr__(self, attr: str) -> t.Any: + return getattr(self.wrapped, attr) + + +def _detect_program_name( + path: str | None = None, _main: ModuleType | None = None +) -> str: + """Determine the command used to run the program, for use in help + text. If a file or entry point was executed, the file name is + returned. If ``python -m`` was used to execute a module or package, + ``python -m name`` is returned. + + This doesn't try to be too precise, the goal is to give a concise + name for help text. Files are only shown as their name without the + path. ``python`` is only shown for modules, and the full path to + ``sys.executable`` is not shown. + + :param path: The Python file being executed. Python puts this in + ``sys.argv[0]``, which is used by default. + :param _main: The ``__main__`` module. This should only be passed + during internal testing. + + .. versionadded:: 8.0 + Based on command args detection in the Werkzeug reloader. + + :meta private: + """ + if _main is None: + _main = sys.modules["__main__"] + + if not path: + path = sys.argv[0] + + # The value of __package__ indicates how Python was called. It may + # not exist if a setuptools script is installed as an egg. It may be + # set incorrectly for entry points created with pip on Windows. + # It is set to "" inside a Shiv or PEX zipapp. + if getattr(_main, "__package__", None) in {None, ""} or ( + os.name == "nt" + and _main.__package__ == "" + and not os.path.exists(path) + and os.path.exists(f"{path}.exe") + ): + # Executed a file, like "python app.py". + return os.path.basename(path) + + # Executed a module, like "python -m example". + # Rewritten by Python from "-m script" to "/path/to/script.py". + # Need to look at main module to determine how it was executed. + py_module = t.cast(str, _main.__package__) + name = os.path.splitext(os.path.basename(path))[0] + + # A submodule like "example.cli". + if name != "__main__": + py_module = f"{py_module}.{name}" + + return f"python -m {py_module.lstrip('.')}" + + +def _expand_args( + args: cabc.Iterable[str], + *, + user: bool = True, + env: bool = True, + glob_recursive: bool = True, +) -> list[str]: + """Simulate Unix shell expansion with Python functions. + + See :func:`glob.glob`, :func:`os.path.expanduser`, and + :func:`os.path.expandvars`. + + This is intended for use on Windows, where the shell does not do any + expansion. It may not exactly match what a Unix shell would do. + + :param args: List of command line arguments to expand. + :param user: Expand user home directory. + :param env: Expand environment variables. + :param glob_recursive: ``**`` matches directories recursively. + + .. versionchanged:: 8.1 + Invalid glob patterns are treated as empty expansions rather + than raising an error. + + .. versionadded:: 8.0 + + :meta private: + """ + from glob import glob + + out = [] + + for arg in args: + if user: + arg = os.path.expanduser(arg) + + if env: + arg = os.path.expandvars(arg) + + try: + matches = glob(arg, recursive=glob_recursive) + except re.error: + matches = [] + + if not matches: + out.append(arg) + else: + out.extend(matches) + + return out diff --git a/venv/lib/python3.12/site-packages/colorama-0.4.6.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/colorama-0.4.6.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/colorama-0.4.6.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.12/site-packages/colorama-0.4.6.dist-info/METADATA b/venv/lib/python3.12/site-packages/colorama-0.4.6.dist-info/METADATA new file mode 100644 index 00000000..a1b5c575 --- /dev/null +++ b/venv/lib/python3.12/site-packages/colorama-0.4.6.dist-info/METADATA @@ -0,0 +1,441 @@ +Metadata-Version: 2.1 +Name: colorama +Version: 0.4.6 +Summary: Cross-platform colored terminal text. +Project-URL: Homepage, https://github.com/tartley/colorama +Author-email: Jonathan Hartley +License-File: LICENSE.txt +Keywords: ansi,color,colour,crossplatform,terminal,text,windows,xplatform +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Console +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Terminals +Requires-Python: !=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7 +Description-Content-Type: text/x-rst + +.. image:: https://img.shields.io/pypi/v/colorama.svg + :target: https://pypi.org/project/colorama/ + :alt: Latest Version + +.. image:: https://img.shields.io/pypi/pyversions/colorama.svg + :target: https://pypi.org/project/colorama/ + :alt: Supported Python versions + +.. image:: https://github.com/tartley/colorama/actions/workflows/test.yml/badge.svg + :target: https://github.com/tartley/colorama/actions/workflows/test.yml + :alt: Build Status + +Colorama +======== + +Makes ANSI escape character sequences (for producing colored terminal text and +cursor positioning) work under MS Windows. + +.. |donate| image:: https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif + :target: https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=2MZ9D2GMLYCUJ&item_name=Colorama¤cy_code=USD + :alt: Donate with Paypal + +`PyPI for releases `_ | +`Github for source `_ | +`Colorama for enterprise on Tidelift `_ + +If you find Colorama useful, please |donate| to the authors. Thank you! + +Installation +------------ + +Tested on CPython 2.7, 3.7, 3.8, 3.9 and 3.10 and Pypy 2.7 and 3.8. + +No requirements other than the standard library. + +.. code-block:: bash + + pip install colorama + # or + conda install -c anaconda colorama + +Description +----------- + +ANSI escape character sequences have long been used to produce colored terminal +text and cursor positioning on Unix and Macs. Colorama makes this work on +Windows, too, by wrapping ``stdout``, stripping ANSI sequences it finds (which +would appear as gobbledygook in the output), and converting them into the +appropriate win32 calls to modify the state of the terminal. On other platforms, +Colorama does nothing. + +This has the upshot of providing a simple cross-platform API for printing +colored terminal text from Python, and has the happy side-effect that existing +applications or libraries which use ANSI sequences to produce colored output on +Linux or Macs can now also work on Windows, simply by calling +``colorama.just_fix_windows_console()`` (since v0.4.6) or ``colorama.init()`` +(all versions, but may have other side-effects – see below). + +An alternative approach is to install ``ansi.sys`` on Windows machines, which +provides the same behaviour for all applications running in terminals. Colorama +is intended for situations where that isn't easy (e.g., maybe your app doesn't +have an installer.) + +Demo scripts in the source code repository print some colored text using +ANSI sequences. Compare their output under Gnome-terminal's built in ANSI +handling, versus on Windows Command-Prompt using Colorama: + +.. image:: https://github.com/tartley/colorama/raw/master/screenshots/ubuntu-demo.png + :width: 661 + :height: 357 + :alt: ANSI sequences on Ubuntu under gnome-terminal. + +.. image:: https://github.com/tartley/colorama/raw/master/screenshots/windows-demo.png + :width: 668 + :height: 325 + :alt: Same ANSI sequences on Windows, using Colorama. + +These screenshots show that, on Windows, Colorama does not support ANSI 'dim +text'; it looks the same as 'normal text'. + +Usage +----- + +Initialisation +.............. + +If the only thing you want from Colorama is to get ANSI escapes to work on +Windows, then run: + +.. code-block:: python + + from colorama import just_fix_windows_console + just_fix_windows_console() + +If you're on a recent version of Windows 10 or better, and your stdout/stderr +are pointing to a Windows console, then this will flip the magic configuration +switch to enable Windows' built-in ANSI support. + +If you're on an older version of Windows, and your stdout/stderr are pointing to +a Windows console, then this will wrap ``sys.stdout`` and/or ``sys.stderr`` in a +magic file object that intercepts ANSI escape sequences and issues the +appropriate Win32 calls to emulate them. + +In all other circumstances, it does nothing whatsoever. Basically the idea is +that this makes Windows act like Unix with respect to ANSI escape handling. + +It's safe to call this function multiple times. It's safe to call this function +on non-Windows platforms, but it won't do anything. It's safe to call this +function when one or both of your stdout/stderr are redirected to a file – it +won't do anything to those streams. + +Alternatively, you can use the older interface with more features (but also more +potential footguns): + +.. code-block:: python + + from colorama import init + init() + +This does the same thing as ``just_fix_windows_console``, except for the +following differences: + +- It's not safe to call ``init`` multiple times; you can end up with multiple + layers of wrapping and broken ANSI support. + +- Colorama will apply a heuristic to guess whether stdout/stderr support ANSI, + and if it thinks they don't, then it will wrap ``sys.stdout`` and + ``sys.stderr`` in a magic file object that strips out ANSI escape sequences + before printing them. This happens on all platforms, and can be convenient if + you want to write your code to emit ANSI escape sequences unconditionally, and + let Colorama decide whether they should actually be output. But note that + Colorama's heuristic is not particularly clever. + +- ``init`` also accepts explicit keyword args to enable/disable various + functionality – see below. + +To stop using Colorama before your program exits, simply call ``deinit()``. +This will restore ``stdout`` and ``stderr`` to their original values, so that +Colorama is disabled. To resume using Colorama again, call ``reinit()``; it is +cheaper than calling ``init()`` again (but does the same thing). + +Most users should depend on ``colorama >= 0.4.6``, and use +``just_fix_windows_console``. The old ``init`` interface will be supported +indefinitely for backwards compatibility, but we don't plan to fix any issues +with it, also for backwards compatibility. + +Colored Output +.............. + +Cross-platform printing of colored text can then be done using Colorama's +constant shorthand for ANSI escape sequences. These are deliberately +rudimentary, see below. + +.. code-block:: python + + from colorama import Fore, Back, Style + print(Fore.RED + 'some red text') + print(Back.GREEN + 'and with a green background') + print(Style.DIM + 'and in dim text') + print(Style.RESET_ALL) + print('back to normal now') + +...or simply by manually printing ANSI sequences from your own code: + +.. code-block:: python + + print('\033[31m' + 'some red text') + print('\033[39m') # and reset to default color + +...or, Colorama can be used in conjunction with existing ANSI libraries +such as the venerable `Termcolor `_ +the fabulous `Blessings `_, +or the incredible `_Rich `_. + +If you wish Colorama's Fore, Back and Style constants were more capable, +then consider using one of the above highly capable libraries to generate +colors, etc, and use Colorama just for its primary purpose: to convert +those ANSI sequences to also work on Windows: + +SIMILARLY, do not send PRs adding the generation of new ANSI types to Colorama. +We are only interested in converting ANSI codes to win32 API calls, not +shortcuts like the above to generate ANSI characters. + +.. code-block:: python + + from colorama import just_fix_windows_console + from termcolor import colored + + # use Colorama to make Termcolor work on Windows too + just_fix_windows_console() + + # then use Termcolor for all colored text output + print(colored('Hello, World!', 'green', 'on_red')) + +Available formatting constants are:: + + Fore: BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, RESET. + Back: BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, RESET. + Style: DIM, NORMAL, BRIGHT, RESET_ALL + +``Style.RESET_ALL`` resets foreground, background, and brightness. Colorama will +perform this reset automatically on program exit. + +These are fairly well supported, but not part of the standard:: + + Fore: LIGHTBLACK_EX, LIGHTRED_EX, LIGHTGREEN_EX, LIGHTYELLOW_EX, LIGHTBLUE_EX, LIGHTMAGENTA_EX, LIGHTCYAN_EX, LIGHTWHITE_EX + Back: LIGHTBLACK_EX, LIGHTRED_EX, LIGHTGREEN_EX, LIGHTYELLOW_EX, LIGHTBLUE_EX, LIGHTMAGENTA_EX, LIGHTCYAN_EX, LIGHTWHITE_EX + +Cursor Positioning +.................. + +ANSI codes to reposition the cursor are supported. See ``demos/demo06.py`` for +an example of how to generate them. + +Init Keyword Args +................. + +``init()`` accepts some ``**kwargs`` to override default behaviour. + +init(autoreset=False): + If you find yourself repeatedly sending reset sequences to turn off color + changes at the end of every print, then ``init(autoreset=True)`` will + automate that: + + .. code-block:: python + + from colorama import init + init(autoreset=True) + print(Fore.RED + 'some red text') + print('automatically back to default color again') + +init(strip=None): + Pass ``True`` or ``False`` to override whether ANSI codes should be + stripped from the output. The default behaviour is to strip if on Windows + or if output is redirected (not a tty). + +init(convert=None): + Pass ``True`` or ``False`` to override whether to convert ANSI codes in the + output into win32 calls. The default behaviour is to convert if on Windows + and output is to a tty (terminal). + +init(wrap=True): + On Windows, Colorama works by replacing ``sys.stdout`` and ``sys.stderr`` + with proxy objects, which override the ``.write()`` method to do their work. + If this wrapping causes you problems, then this can be disabled by passing + ``init(wrap=False)``. The default behaviour is to wrap if ``autoreset`` or + ``strip`` or ``convert`` are True. + + When wrapping is disabled, colored printing on non-Windows platforms will + continue to work as normal. To do cross-platform colored output, you can + use Colorama's ``AnsiToWin32`` proxy directly: + + .. code-block:: python + + import sys + from colorama import init, AnsiToWin32 + init(wrap=False) + stream = AnsiToWin32(sys.stderr).stream + + # Python 2 + print >>stream, Fore.BLUE + 'blue text on stderr' + + # Python 3 + print(Fore.BLUE + 'blue text on stderr', file=stream) + +Recognised ANSI Sequences +......................... + +ANSI sequences generally take the form:: + + ESC [ ; ... + +Where ```` is an integer, and ```` is a single letter. Zero or +more params are passed to a ````. If no params are passed, it is +generally synonymous with passing a single zero. No spaces exist in the +sequence; they have been inserted here simply to read more easily. + +The only ANSI sequences that Colorama converts into win32 calls are:: + + ESC [ 0 m # reset all (colors and brightness) + ESC [ 1 m # bright + ESC [ 2 m # dim (looks same as normal brightness) + ESC [ 22 m # normal brightness + + # FOREGROUND: + ESC [ 30 m # black + ESC [ 31 m # red + ESC [ 32 m # green + ESC [ 33 m # yellow + ESC [ 34 m # blue + ESC [ 35 m # magenta + ESC [ 36 m # cyan + ESC [ 37 m # white + ESC [ 39 m # reset + + # BACKGROUND + ESC [ 40 m # black + ESC [ 41 m # red + ESC [ 42 m # green + ESC [ 43 m # yellow + ESC [ 44 m # blue + ESC [ 45 m # magenta + ESC [ 46 m # cyan + ESC [ 47 m # white + ESC [ 49 m # reset + + # cursor positioning + ESC [ y;x H # position cursor at x across, y down + ESC [ y;x f # position cursor at x across, y down + ESC [ n A # move cursor n lines up + ESC [ n B # move cursor n lines down + ESC [ n C # move cursor n characters forward + ESC [ n D # move cursor n characters backward + + # clear the screen + ESC [ mode J # clear the screen + + # clear the line + ESC [ mode K # clear the line + +Multiple numeric params to the ``'m'`` command can be combined into a single +sequence:: + + ESC [ 36 ; 45 ; 1 m # bright cyan text on magenta background + +All other ANSI sequences of the form ``ESC [ ; ... `` +are silently stripped from the output on Windows. + +Any other form of ANSI sequence, such as single-character codes or alternative +initial characters, are not recognised or stripped. It would be cool to add +them though. Let me know if it would be useful for you, via the Issues on +GitHub. + +Status & Known Problems +----------------------- + +I've personally only tested it on Windows XP (CMD, Console2), Ubuntu +(gnome-terminal, xterm), and OS X. + +Some valid ANSI sequences aren't recognised. + +If you're hacking on the code, see `README-hacking.md`_. ESPECIALLY, see the +explanation there of why we do not want PRs that allow Colorama to generate new +types of ANSI codes. + +See outstanding issues and wish-list: +https://github.com/tartley/colorama/issues + +If anything doesn't work for you, or doesn't do what you expected or hoped for, +I'd love to hear about it on that issues list, would be delighted by patches, +and would be happy to grant commit access to anyone who submits a working patch +or two. + +.. _README-hacking.md: README-hacking.md + +License +------- + +Copyright Jonathan Hartley & Arnon Yaari, 2013-2020. BSD 3-Clause license; see +LICENSE file. + +Professional support +-------------------- + +.. |tideliftlogo| image:: https://cdn2.hubspot.net/hubfs/4008838/website/logos/logos_for_download/Tidelift_primary-shorthand-logo.png + :alt: Tidelift + :target: https://tidelift.com/subscription/pkg/pypi-colorama?utm_source=pypi-colorama&utm_medium=referral&utm_campaign=readme + +.. list-table:: + :widths: 10 100 + + * - |tideliftlogo| + - Professional support for colorama is available as part of the + `Tidelift Subscription`_. + Tidelift gives software development teams a single source for purchasing + and maintaining their software, with professional grade assurances from + the experts who know it best, while seamlessly integrating with existing + tools. + +.. _Tidelift Subscription: https://tidelift.com/subscription/pkg/pypi-colorama?utm_source=pypi-colorama&utm_medium=referral&utm_campaign=readme + +Thanks +------ + +See the CHANGELOG for more thanks! + +* Marc Schlaich (schlamar) for a ``setup.py`` fix for Python2.5. +* Marc Abramowitz, reported & fixed a crash on exit with closed ``stdout``, + providing a solution to issue #7's setuptools/distutils debate, + and other fixes. +* User 'eryksun', for guidance on correctly instantiating ``ctypes.windll``. +* Matthew McCormick for politely pointing out a longstanding crash on non-Win. +* Ben Hoyt, for a magnificent fix under 64-bit Windows. +* Jesse at Empty Square for submitting a fix for examples in the README. +* User 'jamessp', an observant documentation fix for cursor positioning. +* User 'vaal1239', Dave Mckee & Lackner Kristof for a tiny but much-needed Win7 + fix. +* Julien Stuyck, for wisely suggesting Python3 compatible updates to README. +* Daniel Griffith for multiple fabulous patches. +* Oscar Lesta for a valuable fix to stop ANSI chars being sent to non-tty + output. +* Roger Binns, for many suggestions, valuable feedback, & bug reports. +* Tim Golden for thought and much appreciated feedback on the initial idea. +* User 'Zearin' for updates to the README file. +* John Szakmeister for adding support for light colors +* Charles Merriam for adding documentation to demos +* Jurko for a fix on 64-bit Windows CPython2.5 w/o ctypes +* Florian Bruhin for a fix when stdout or stderr are None +* Thomas Weininger for fixing ValueError on Windows +* Remi Rampin for better Github integration and fixes to the README file +* Simeon Visser for closing a file handle using 'with' and updating classifiers + to include Python 3.3 and 3.4 +* Andy Neff for fixing RESET of LIGHT_EX colors. +* Jonathan Hartley for the initial idea and implementation. diff --git a/venv/lib/python3.12/site-packages/colorama-0.4.6.dist-info/RECORD b/venv/lib/python3.12/site-packages/colorama-0.4.6.dist-info/RECORD new file mode 100644 index 00000000..496de272 --- /dev/null +++ b/venv/lib/python3.12/site-packages/colorama-0.4.6.dist-info/RECORD @@ -0,0 +1,31 @@ +colorama-0.4.6.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +colorama-0.4.6.dist-info/METADATA,sha256=e67SnrUMOym9sz_4TjF3vxvAV4T3aF7NyqRHHH3YEMw,17158 +colorama-0.4.6.dist-info/RECORD,, +colorama-0.4.6.dist-info/WHEEL,sha256=cdcF4Fbd0FPtw2EMIOwH-3rSOTUdTCeOSXRMD1iLUb8,105 +colorama-0.4.6.dist-info/licenses/LICENSE.txt,sha256=ysNcAmhuXQSlpxQL-zs25zrtSWZW6JEQLkKIhteTAxg,1491 +colorama/__init__.py,sha256=wePQA4U20tKgYARySLEC047ucNX-g8pRLpYBuiHlLb8,266 +colorama/__pycache__/__init__.cpython-312.pyc,, +colorama/__pycache__/ansi.cpython-312.pyc,, +colorama/__pycache__/ansitowin32.cpython-312.pyc,, +colorama/__pycache__/initialise.cpython-312.pyc,, +colorama/__pycache__/win32.cpython-312.pyc,, +colorama/__pycache__/winterm.cpython-312.pyc,, +colorama/ansi.py,sha256=Top4EeEuaQdBWdteKMEcGOTeKeF19Q-Wo_6_Cj5kOzQ,2522 +colorama/ansitowin32.py,sha256=vPNYa3OZbxjbuFyaVo0Tmhmy1FZ1lKMWCnT7odXpItk,11128 +colorama/initialise.py,sha256=-hIny86ClXo39ixh5iSCfUIa2f_h_bgKRDW7gqs-KLU,3325 +colorama/tests/__init__.py,sha256=MkgPAEzGQd-Rq0w0PZXSX2LadRWhUECcisJY8lSrm4Q,75 +colorama/tests/__pycache__/__init__.cpython-312.pyc,, +colorama/tests/__pycache__/ansi_test.cpython-312.pyc,, +colorama/tests/__pycache__/ansitowin32_test.cpython-312.pyc,, +colorama/tests/__pycache__/initialise_test.cpython-312.pyc,, +colorama/tests/__pycache__/isatty_test.cpython-312.pyc,, +colorama/tests/__pycache__/utils.cpython-312.pyc,, +colorama/tests/__pycache__/winterm_test.cpython-312.pyc,, +colorama/tests/ansi_test.py,sha256=FeViDrUINIZcr505PAxvU4AjXz1asEiALs9GXMhwRaE,2839 +colorama/tests/ansitowin32_test.py,sha256=RN7AIhMJ5EqDsYaCjVo-o4u8JzDD4ukJbmevWKS70rY,10678 +colorama/tests/initialise_test.py,sha256=BbPy-XfyHwJ6zKozuQOvNvQZzsx9vdb_0bYXn7hsBTc,6741 +colorama/tests/isatty_test.py,sha256=Pg26LRpv0yQDB5Ac-sxgVXG7hsA1NYvapFgApZfYzZg,1866 +colorama/tests/utils.py,sha256=1IIRylG39z5-dzq09R_ngufxyPZxgldNbrxKxUGwGKE,1079 +colorama/tests/winterm_test.py,sha256=qoWFPEjym5gm2RuMwpf3pOis3a5r_PJZFCzK254JL8A,3709 +colorama/win32.py,sha256=YQOKwMTwtGBbsY4dL5HYTvwTeP9wIQra5MvPNddpxZs,6181 +colorama/winterm.py,sha256=XCQFDHjPi6AHYNdZwy0tA02H-Jh48Jp-HvCjeLeLp3U,7134 diff --git a/venv/lib/python3.12/site-packages/colorama-0.4.6.dist-info/WHEEL b/venv/lib/python3.12/site-packages/colorama-0.4.6.dist-info/WHEEL new file mode 100644 index 00000000..d79189fd --- /dev/null +++ b/venv/lib/python3.12/site-packages/colorama-0.4.6.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.11.1 +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any diff --git a/venv/lib/python3.12/site-packages/colorama-0.4.6.dist-info/licenses/LICENSE.txt b/venv/lib/python3.12/site-packages/colorama-0.4.6.dist-info/licenses/LICENSE.txt new file mode 100644 index 00000000..3105888e --- /dev/null +++ b/venv/lib/python3.12/site-packages/colorama-0.4.6.dist-info/licenses/LICENSE.txt @@ -0,0 +1,27 @@ +Copyright (c) 2010 Jonathan Hartley +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holders, nor those of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/lib/python3.12/site-packages/colorama/__init__.py b/venv/lib/python3.12/site-packages/colorama/__init__.py new file mode 100644 index 00000000..383101cd --- /dev/null +++ b/venv/lib/python3.12/site-packages/colorama/__init__.py @@ -0,0 +1,7 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +from .initialise import init, deinit, reinit, colorama_text, just_fix_windows_console +from .ansi import Fore, Back, Style, Cursor +from .ansitowin32 import AnsiToWin32 + +__version__ = '0.4.6' + diff --git a/venv/lib/python3.12/site-packages/colorama/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/colorama/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 00000000..75f98fc7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/colorama/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/colorama/__pycache__/ansi.cpython-312.pyc b/venv/lib/python3.12/site-packages/colorama/__pycache__/ansi.cpython-312.pyc new file mode 100644 index 00000000..5b31442a Binary files /dev/null and b/venv/lib/python3.12/site-packages/colorama/__pycache__/ansi.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/colorama/__pycache__/ansitowin32.cpython-312.pyc b/venv/lib/python3.12/site-packages/colorama/__pycache__/ansitowin32.cpython-312.pyc new file mode 100644 index 00000000..e6e0a604 Binary files /dev/null and b/venv/lib/python3.12/site-packages/colorama/__pycache__/ansitowin32.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/colorama/__pycache__/initialise.cpython-312.pyc b/venv/lib/python3.12/site-packages/colorama/__pycache__/initialise.cpython-312.pyc new file mode 100644 index 00000000..d2e3686b Binary files /dev/null and b/venv/lib/python3.12/site-packages/colorama/__pycache__/initialise.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/colorama/__pycache__/win32.cpython-312.pyc b/venv/lib/python3.12/site-packages/colorama/__pycache__/win32.cpython-312.pyc new file mode 100644 index 00000000..60f3262d Binary files /dev/null and b/venv/lib/python3.12/site-packages/colorama/__pycache__/win32.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/colorama/__pycache__/winterm.cpython-312.pyc b/venv/lib/python3.12/site-packages/colorama/__pycache__/winterm.cpython-312.pyc new file mode 100644 index 00000000..c0ba8082 Binary files /dev/null and b/venv/lib/python3.12/site-packages/colorama/__pycache__/winterm.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/colorama/ansi.py b/venv/lib/python3.12/site-packages/colorama/ansi.py new file mode 100644 index 00000000..11ec695f --- /dev/null +++ b/venv/lib/python3.12/site-packages/colorama/ansi.py @@ -0,0 +1,102 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +''' +This module generates ANSI character codes to printing colors to terminals. +See: http://en.wikipedia.org/wiki/ANSI_escape_code +''' + +CSI = '\033[' +OSC = '\033]' +BEL = '\a' + + +def code_to_chars(code): + return CSI + str(code) + 'm' + +def set_title(title): + return OSC + '2;' + title + BEL + +def clear_screen(mode=2): + return CSI + str(mode) + 'J' + +def clear_line(mode=2): + return CSI + str(mode) + 'K' + + +class AnsiCodes(object): + def __init__(self): + # the subclasses declare class attributes which are numbers. + # Upon instantiation we define instance attributes, which are the same + # as the class attributes but wrapped with the ANSI escape sequence + for name in dir(self): + if not name.startswith('_'): + value = getattr(self, name) + setattr(self, name, code_to_chars(value)) + + +class AnsiCursor(object): + def UP(self, n=1): + return CSI + str(n) + 'A' + def DOWN(self, n=1): + return CSI + str(n) + 'B' + def FORWARD(self, n=1): + return CSI + str(n) + 'C' + def BACK(self, n=1): + return CSI + str(n) + 'D' + def POS(self, x=1, y=1): + return CSI + str(y) + ';' + str(x) + 'H' + + +class AnsiFore(AnsiCodes): + BLACK = 30 + RED = 31 + GREEN = 32 + YELLOW = 33 + BLUE = 34 + MAGENTA = 35 + CYAN = 36 + WHITE = 37 + RESET = 39 + + # These are fairly well supported, but not part of the standard. + LIGHTBLACK_EX = 90 + LIGHTRED_EX = 91 + LIGHTGREEN_EX = 92 + LIGHTYELLOW_EX = 93 + LIGHTBLUE_EX = 94 + LIGHTMAGENTA_EX = 95 + LIGHTCYAN_EX = 96 + LIGHTWHITE_EX = 97 + + +class AnsiBack(AnsiCodes): + BLACK = 40 + RED = 41 + GREEN = 42 + YELLOW = 43 + BLUE = 44 + MAGENTA = 45 + CYAN = 46 + WHITE = 47 + RESET = 49 + + # These are fairly well supported, but not part of the standard. + LIGHTBLACK_EX = 100 + LIGHTRED_EX = 101 + LIGHTGREEN_EX = 102 + LIGHTYELLOW_EX = 103 + LIGHTBLUE_EX = 104 + LIGHTMAGENTA_EX = 105 + LIGHTCYAN_EX = 106 + LIGHTWHITE_EX = 107 + + +class AnsiStyle(AnsiCodes): + BRIGHT = 1 + DIM = 2 + NORMAL = 22 + RESET_ALL = 0 + +Fore = AnsiFore() +Back = AnsiBack() +Style = AnsiStyle() +Cursor = AnsiCursor() diff --git a/venv/lib/python3.12/site-packages/colorama/ansitowin32.py b/venv/lib/python3.12/site-packages/colorama/ansitowin32.py new file mode 100644 index 00000000..abf209e6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/colorama/ansitowin32.py @@ -0,0 +1,277 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +import re +import sys +import os + +from .ansi import AnsiFore, AnsiBack, AnsiStyle, Style, BEL +from .winterm import enable_vt_processing, WinTerm, WinColor, WinStyle +from .win32 import windll, winapi_test + + +winterm = None +if windll is not None: + winterm = WinTerm() + + +class StreamWrapper(object): + ''' + Wraps a stream (such as stdout), acting as a transparent proxy for all + attribute access apart from method 'write()', which is delegated to our + Converter instance. + ''' + def __init__(self, wrapped, converter): + # double-underscore everything to prevent clashes with names of + # attributes on the wrapped stream object. + self.__wrapped = wrapped + self.__convertor = converter + + def __getattr__(self, name): + return getattr(self.__wrapped, name) + + def __enter__(self, *args, **kwargs): + # special method lookup bypasses __getattr__/__getattribute__, see + # https://stackoverflow.com/questions/12632894/why-doesnt-getattr-work-with-exit + # thus, contextlib magic methods are not proxied via __getattr__ + return self.__wrapped.__enter__(*args, **kwargs) + + def __exit__(self, *args, **kwargs): + return self.__wrapped.__exit__(*args, **kwargs) + + def __setstate__(self, state): + self.__dict__ = state + + def __getstate__(self): + return self.__dict__ + + def write(self, text): + self.__convertor.write(text) + + def isatty(self): + stream = self.__wrapped + if 'PYCHARM_HOSTED' in os.environ: + if stream is not None and (stream is sys.__stdout__ or stream is sys.__stderr__): + return True + try: + stream_isatty = stream.isatty + except AttributeError: + return False + else: + return stream_isatty() + + @property + def closed(self): + stream = self.__wrapped + try: + return stream.closed + # AttributeError in the case that the stream doesn't support being closed + # ValueError for the case that the stream has already been detached when atexit runs + except (AttributeError, ValueError): + return True + + +class AnsiToWin32(object): + ''' + Implements a 'write()' method which, on Windows, will strip ANSI character + sequences from the text, and if outputting to a tty, will convert them into + win32 function calls. + ''' + ANSI_CSI_RE = re.compile('\001?\033\\[((?:\\d|;)*)([a-zA-Z])\002?') # Control Sequence Introducer + ANSI_OSC_RE = re.compile('\001?\033\\]([^\a]*)(\a)\002?') # Operating System Command + + def __init__(self, wrapped, convert=None, strip=None, autoreset=False): + # The wrapped stream (normally sys.stdout or sys.stderr) + self.wrapped = wrapped + + # should we reset colors to defaults after every .write() + self.autoreset = autoreset + + # create the proxy wrapping our output stream + self.stream = StreamWrapper(wrapped, self) + + on_windows = os.name == 'nt' + # We test if the WinAPI works, because even if we are on Windows + # we may be using a terminal that doesn't support the WinAPI + # (e.g. Cygwin Terminal). In this case it's up to the terminal + # to support the ANSI codes. + conversion_supported = on_windows and winapi_test() + try: + fd = wrapped.fileno() + except Exception: + fd = -1 + system_has_native_ansi = not on_windows or enable_vt_processing(fd) + have_tty = not self.stream.closed and self.stream.isatty() + need_conversion = conversion_supported and not system_has_native_ansi + + # should we strip ANSI sequences from our output? + if strip is None: + strip = need_conversion or not have_tty + self.strip = strip + + # should we should convert ANSI sequences into win32 calls? + if convert is None: + convert = need_conversion and have_tty + self.convert = convert + + # dict of ansi codes to win32 functions and parameters + self.win32_calls = self.get_win32_calls() + + # are we wrapping stderr? + self.on_stderr = self.wrapped is sys.stderr + + def should_wrap(self): + ''' + True if this class is actually needed. If false, then the output + stream will not be affected, nor will win32 calls be issued, so + wrapping stdout is not actually required. This will generally be + False on non-Windows platforms, unless optional functionality like + autoreset has been requested using kwargs to init() + ''' + return self.convert or self.strip or self.autoreset + + def get_win32_calls(self): + if self.convert and winterm: + return { + AnsiStyle.RESET_ALL: (winterm.reset_all, ), + AnsiStyle.BRIGHT: (winterm.style, WinStyle.BRIGHT), + AnsiStyle.DIM: (winterm.style, WinStyle.NORMAL), + AnsiStyle.NORMAL: (winterm.style, WinStyle.NORMAL), + AnsiFore.BLACK: (winterm.fore, WinColor.BLACK), + AnsiFore.RED: (winterm.fore, WinColor.RED), + AnsiFore.GREEN: (winterm.fore, WinColor.GREEN), + AnsiFore.YELLOW: (winterm.fore, WinColor.YELLOW), + AnsiFore.BLUE: (winterm.fore, WinColor.BLUE), + AnsiFore.MAGENTA: (winterm.fore, WinColor.MAGENTA), + AnsiFore.CYAN: (winterm.fore, WinColor.CYAN), + AnsiFore.WHITE: (winterm.fore, WinColor.GREY), + AnsiFore.RESET: (winterm.fore, ), + AnsiFore.LIGHTBLACK_EX: (winterm.fore, WinColor.BLACK, True), + AnsiFore.LIGHTRED_EX: (winterm.fore, WinColor.RED, True), + AnsiFore.LIGHTGREEN_EX: (winterm.fore, WinColor.GREEN, True), + AnsiFore.LIGHTYELLOW_EX: (winterm.fore, WinColor.YELLOW, True), + AnsiFore.LIGHTBLUE_EX: (winterm.fore, WinColor.BLUE, True), + AnsiFore.LIGHTMAGENTA_EX: (winterm.fore, WinColor.MAGENTA, True), + AnsiFore.LIGHTCYAN_EX: (winterm.fore, WinColor.CYAN, True), + AnsiFore.LIGHTWHITE_EX: (winterm.fore, WinColor.GREY, True), + AnsiBack.BLACK: (winterm.back, WinColor.BLACK), + AnsiBack.RED: (winterm.back, WinColor.RED), + AnsiBack.GREEN: (winterm.back, WinColor.GREEN), + AnsiBack.YELLOW: (winterm.back, WinColor.YELLOW), + AnsiBack.BLUE: (winterm.back, WinColor.BLUE), + AnsiBack.MAGENTA: (winterm.back, WinColor.MAGENTA), + AnsiBack.CYAN: (winterm.back, WinColor.CYAN), + AnsiBack.WHITE: (winterm.back, WinColor.GREY), + AnsiBack.RESET: (winterm.back, ), + AnsiBack.LIGHTBLACK_EX: (winterm.back, WinColor.BLACK, True), + AnsiBack.LIGHTRED_EX: (winterm.back, WinColor.RED, True), + AnsiBack.LIGHTGREEN_EX: (winterm.back, WinColor.GREEN, True), + AnsiBack.LIGHTYELLOW_EX: (winterm.back, WinColor.YELLOW, True), + AnsiBack.LIGHTBLUE_EX: (winterm.back, WinColor.BLUE, True), + AnsiBack.LIGHTMAGENTA_EX: (winterm.back, WinColor.MAGENTA, True), + AnsiBack.LIGHTCYAN_EX: (winterm.back, WinColor.CYAN, True), + AnsiBack.LIGHTWHITE_EX: (winterm.back, WinColor.GREY, True), + } + return dict() + + def write(self, text): + if self.strip or self.convert: + self.write_and_convert(text) + else: + self.wrapped.write(text) + self.wrapped.flush() + if self.autoreset: + self.reset_all() + + + def reset_all(self): + if self.convert: + self.call_win32('m', (0,)) + elif not self.strip and not self.stream.closed: + self.wrapped.write(Style.RESET_ALL) + + + def write_and_convert(self, text): + ''' + Write the given text to our wrapped stream, stripping any ANSI + sequences from the text, and optionally converting them into win32 + calls. + ''' + cursor = 0 + text = self.convert_osc(text) + for match in self.ANSI_CSI_RE.finditer(text): + start, end = match.span() + self.write_plain_text(text, cursor, start) + self.convert_ansi(*match.groups()) + cursor = end + self.write_plain_text(text, cursor, len(text)) + + + def write_plain_text(self, text, start, end): + if start < end: + self.wrapped.write(text[start:end]) + self.wrapped.flush() + + + def convert_ansi(self, paramstring, command): + if self.convert: + params = self.extract_params(command, paramstring) + self.call_win32(command, params) + + + def extract_params(self, command, paramstring): + if command in 'Hf': + params = tuple(int(p) if len(p) != 0 else 1 for p in paramstring.split(';')) + while len(params) < 2: + # defaults: + params = params + (1,) + else: + params = tuple(int(p) for p in paramstring.split(';') if len(p) != 0) + if len(params) == 0: + # defaults: + if command in 'JKm': + params = (0,) + elif command in 'ABCD': + params = (1,) + + return params + + + def call_win32(self, command, params): + if command == 'm': + for param in params: + if param in self.win32_calls: + func_args = self.win32_calls[param] + func = func_args[0] + args = func_args[1:] + kwargs = dict(on_stderr=self.on_stderr) + func(*args, **kwargs) + elif command in 'J': + winterm.erase_screen(params[0], on_stderr=self.on_stderr) + elif command in 'K': + winterm.erase_line(params[0], on_stderr=self.on_stderr) + elif command in 'Hf': # cursor position - absolute + winterm.set_cursor_position(params, on_stderr=self.on_stderr) + elif command in 'ABCD': # cursor position - relative + n = params[0] + # A - up, B - down, C - forward, D - back + x, y = {'A': (0, -n), 'B': (0, n), 'C': (n, 0), 'D': (-n, 0)}[command] + winterm.cursor_adjust(x, y, on_stderr=self.on_stderr) + + + def convert_osc(self, text): + for match in self.ANSI_OSC_RE.finditer(text): + start, end = match.span() + text = text[:start] + text[end:] + paramstring, command = match.groups() + if command == BEL: + if paramstring.count(";") == 1: + params = paramstring.split(";") + # 0 - change title and icon (we will only change title) + # 1 - change icon (we don't support this) + # 2 - change title + if params[0] in '02': + winterm.set_title(params[1]) + return text + + + def flush(self): + self.wrapped.flush() diff --git a/venv/lib/python3.12/site-packages/colorama/initialise.py b/venv/lib/python3.12/site-packages/colorama/initialise.py new file mode 100644 index 00000000..d5fd4b71 --- /dev/null +++ b/venv/lib/python3.12/site-packages/colorama/initialise.py @@ -0,0 +1,121 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +import atexit +import contextlib +import sys + +from .ansitowin32 import AnsiToWin32 + + +def _wipe_internal_state_for_tests(): + global orig_stdout, orig_stderr + orig_stdout = None + orig_stderr = None + + global wrapped_stdout, wrapped_stderr + wrapped_stdout = None + wrapped_stderr = None + + global atexit_done + atexit_done = False + + global fixed_windows_console + fixed_windows_console = False + + try: + # no-op if it wasn't registered + atexit.unregister(reset_all) + except AttributeError: + # python 2: no atexit.unregister. Oh well, we did our best. + pass + + +def reset_all(): + if AnsiToWin32 is not None: # Issue #74: objects might become None at exit + AnsiToWin32(orig_stdout).reset_all() + + +def init(autoreset=False, convert=None, strip=None, wrap=True): + + if not wrap and any([autoreset, convert, strip]): + raise ValueError('wrap=False conflicts with any other arg=True') + + global wrapped_stdout, wrapped_stderr + global orig_stdout, orig_stderr + + orig_stdout = sys.stdout + orig_stderr = sys.stderr + + if sys.stdout is None: + wrapped_stdout = None + else: + sys.stdout = wrapped_stdout = \ + wrap_stream(orig_stdout, convert, strip, autoreset, wrap) + if sys.stderr is None: + wrapped_stderr = None + else: + sys.stderr = wrapped_stderr = \ + wrap_stream(orig_stderr, convert, strip, autoreset, wrap) + + global atexit_done + if not atexit_done: + atexit.register(reset_all) + atexit_done = True + + +def deinit(): + if orig_stdout is not None: + sys.stdout = orig_stdout + if orig_stderr is not None: + sys.stderr = orig_stderr + + +def just_fix_windows_console(): + global fixed_windows_console + + if sys.platform != "win32": + return + if fixed_windows_console: + return + if wrapped_stdout is not None or wrapped_stderr is not None: + # Someone already ran init() and it did stuff, so we won't second-guess them + return + + # On newer versions of Windows, AnsiToWin32.__init__ will implicitly enable the + # native ANSI support in the console as a side-effect. We only need to actually + # replace sys.stdout/stderr if we're in the old-style conversion mode. + new_stdout = AnsiToWin32(sys.stdout, convert=None, strip=None, autoreset=False) + if new_stdout.convert: + sys.stdout = new_stdout + new_stderr = AnsiToWin32(sys.stderr, convert=None, strip=None, autoreset=False) + if new_stderr.convert: + sys.stderr = new_stderr + + fixed_windows_console = True + +@contextlib.contextmanager +def colorama_text(*args, **kwargs): + init(*args, **kwargs) + try: + yield + finally: + deinit() + + +def reinit(): + if wrapped_stdout is not None: + sys.stdout = wrapped_stdout + if wrapped_stderr is not None: + sys.stderr = wrapped_stderr + + +def wrap_stream(stream, convert, strip, autoreset, wrap): + if wrap: + wrapper = AnsiToWin32(stream, + convert=convert, strip=strip, autoreset=autoreset) + if wrapper.should_wrap(): + stream = wrapper.stream + return stream + + +# Use this for initial setup as well, to reduce code duplication +_wipe_internal_state_for_tests() diff --git a/venv/lib/python3.12/site-packages/colorama/tests/__init__.py b/venv/lib/python3.12/site-packages/colorama/tests/__init__.py new file mode 100644 index 00000000..8c5661e9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/colorama/tests/__init__.py @@ -0,0 +1 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. diff --git a/venv/lib/python3.12/site-packages/colorama/tests/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/colorama/tests/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 00000000..307f17cc Binary files /dev/null and b/venv/lib/python3.12/site-packages/colorama/tests/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/colorama/tests/__pycache__/ansi_test.cpython-312.pyc b/venv/lib/python3.12/site-packages/colorama/tests/__pycache__/ansi_test.cpython-312.pyc new file mode 100644 index 00000000..3c1d2681 Binary files /dev/null and b/venv/lib/python3.12/site-packages/colorama/tests/__pycache__/ansi_test.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/colorama/tests/__pycache__/ansitowin32_test.cpython-312.pyc b/venv/lib/python3.12/site-packages/colorama/tests/__pycache__/ansitowin32_test.cpython-312.pyc new file mode 100644 index 00000000..2b77a6d9 Binary files /dev/null and b/venv/lib/python3.12/site-packages/colorama/tests/__pycache__/ansitowin32_test.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/colorama/tests/__pycache__/initialise_test.cpython-312.pyc b/venv/lib/python3.12/site-packages/colorama/tests/__pycache__/initialise_test.cpython-312.pyc new file mode 100644 index 00000000..37b40da0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/colorama/tests/__pycache__/initialise_test.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/colorama/tests/__pycache__/isatty_test.cpython-312.pyc b/venv/lib/python3.12/site-packages/colorama/tests/__pycache__/isatty_test.cpython-312.pyc new file mode 100644 index 00000000..5fb9bd21 Binary files /dev/null and b/venv/lib/python3.12/site-packages/colorama/tests/__pycache__/isatty_test.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/colorama/tests/__pycache__/utils.cpython-312.pyc b/venv/lib/python3.12/site-packages/colorama/tests/__pycache__/utils.cpython-312.pyc new file mode 100644 index 00000000..b037de73 Binary files /dev/null and b/venv/lib/python3.12/site-packages/colorama/tests/__pycache__/utils.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/colorama/tests/__pycache__/winterm_test.cpython-312.pyc b/venv/lib/python3.12/site-packages/colorama/tests/__pycache__/winterm_test.cpython-312.pyc new file mode 100644 index 00000000..c1c5e1b5 Binary files /dev/null and b/venv/lib/python3.12/site-packages/colorama/tests/__pycache__/winterm_test.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/colorama/tests/ansi_test.py b/venv/lib/python3.12/site-packages/colorama/tests/ansi_test.py new file mode 100644 index 00000000..0a20c80f --- /dev/null +++ b/venv/lib/python3.12/site-packages/colorama/tests/ansi_test.py @@ -0,0 +1,76 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +import sys +from unittest import TestCase, main + +from ..ansi import Back, Fore, Style +from ..ansitowin32 import AnsiToWin32 + +stdout_orig = sys.stdout +stderr_orig = sys.stderr + + +class AnsiTest(TestCase): + + def setUp(self): + # sanity check: stdout should be a file or StringIO object. + # It will only be AnsiToWin32 if init() has previously wrapped it + self.assertNotEqual(type(sys.stdout), AnsiToWin32) + self.assertNotEqual(type(sys.stderr), AnsiToWin32) + + def tearDown(self): + sys.stdout = stdout_orig + sys.stderr = stderr_orig + + + def testForeAttributes(self): + self.assertEqual(Fore.BLACK, '\033[30m') + self.assertEqual(Fore.RED, '\033[31m') + self.assertEqual(Fore.GREEN, '\033[32m') + self.assertEqual(Fore.YELLOW, '\033[33m') + self.assertEqual(Fore.BLUE, '\033[34m') + self.assertEqual(Fore.MAGENTA, '\033[35m') + self.assertEqual(Fore.CYAN, '\033[36m') + self.assertEqual(Fore.WHITE, '\033[37m') + self.assertEqual(Fore.RESET, '\033[39m') + + # Check the light, extended versions. + self.assertEqual(Fore.LIGHTBLACK_EX, '\033[90m') + self.assertEqual(Fore.LIGHTRED_EX, '\033[91m') + self.assertEqual(Fore.LIGHTGREEN_EX, '\033[92m') + self.assertEqual(Fore.LIGHTYELLOW_EX, '\033[93m') + self.assertEqual(Fore.LIGHTBLUE_EX, '\033[94m') + self.assertEqual(Fore.LIGHTMAGENTA_EX, '\033[95m') + self.assertEqual(Fore.LIGHTCYAN_EX, '\033[96m') + self.assertEqual(Fore.LIGHTWHITE_EX, '\033[97m') + + + def testBackAttributes(self): + self.assertEqual(Back.BLACK, '\033[40m') + self.assertEqual(Back.RED, '\033[41m') + self.assertEqual(Back.GREEN, '\033[42m') + self.assertEqual(Back.YELLOW, '\033[43m') + self.assertEqual(Back.BLUE, '\033[44m') + self.assertEqual(Back.MAGENTA, '\033[45m') + self.assertEqual(Back.CYAN, '\033[46m') + self.assertEqual(Back.WHITE, '\033[47m') + self.assertEqual(Back.RESET, '\033[49m') + + # Check the light, extended versions. + self.assertEqual(Back.LIGHTBLACK_EX, '\033[100m') + self.assertEqual(Back.LIGHTRED_EX, '\033[101m') + self.assertEqual(Back.LIGHTGREEN_EX, '\033[102m') + self.assertEqual(Back.LIGHTYELLOW_EX, '\033[103m') + self.assertEqual(Back.LIGHTBLUE_EX, '\033[104m') + self.assertEqual(Back.LIGHTMAGENTA_EX, '\033[105m') + self.assertEqual(Back.LIGHTCYAN_EX, '\033[106m') + self.assertEqual(Back.LIGHTWHITE_EX, '\033[107m') + + + def testStyleAttributes(self): + self.assertEqual(Style.DIM, '\033[2m') + self.assertEqual(Style.NORMAL, '\033[22m') + self.assertEqual(Style.BRIGHT, '\033[1m') + + +if __name__ == '__main__': + main() diff --git a/venv/lib/python3.12/site-packages/colorama/tests/ansitowin32_test.py b/venv/lib/python3.12/site-packages/colorama/tests/ansitowin32_test.py new file mode 100644 index 00000000..91ca551f --- /dev/null +++ b/venv/lib/python3.12/site-packages/colorama/tests/ansitowin32_test.py @@ -0,0 +1,294 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +from io import StringIO, TextIOWrapper +from unittest import TestCase, main +try: + from contextlib import ExitStack +except ImportError: + # python 2 + from contextlib2 import ExitStack + +try: + from unittest.mock import MagicMock, Mock, patch +except ImportError: + from mock import MagicMock, Mock, patch + +from ..ansitowin32 import AnsiToWin32, StreamWrapper +from ..win32 import ENABLE_VIRTUAL_TERMINAL_PROCESSING +from .utils import osname + + +class StreamWrapperTest(TestCase): + + def testIsAProxy(self): + mockStream = Mock() + wrapper = StreamWrapper(mockStream, None) + self.assertTrue( wrapper.random_attr is mockStream.random_attr ) + + def testDelegatesWrite(self): + mockStream = Mock() + mockConverter = Mock() + wrapper = StreamWrapper(mockStream, mockConverter) + wrapper.write('hello') + self.assertTrue(mockConverter.write.call_args, (('hello',), {})) + + def testDelegatesContext(self): + mockConverter = Mock() + s = StringIO() + with StreamWrapper(s, mockConverter) as fp: + fp.write(u'hello') + self.assertTrue(s.closed) + + def testProxyNoContextManager(self): + mockStream = MagicMock() + mockStream.__enter__.side_effect = AttributeError() + mockConverter = Mock() + with self.assertRaises(AttributeError) as excinfo: + with StreamWrapper(mockStream, mockConverter) as wrapper: + wrapper.write('hello') + + def test_closed_shouldnt_raise_on_closed_stream(self): + stream = StringIO() + stream.close() + wrapper = StreamWrapper(stream, None) + self.assertEqual(wrapper.closed, True) + + def test_closed_shouldnt_raise_on_detached_stream(self): + stream = TextIOWrapper(StringIO()) + stream.detach() + wrapper = StreamWrapper(stream, None) + self.assertEqual(wrapper.closed, True) + +class AnsiToWin32Test(TestCase): + + def testInit(self): + mockStdout = Mock() + auto = Mock() + stream = AnsiToWin32(mockStdout, autoreset=auto) + self.assertEqual(stream.wrapped, mockStdout) + self.assertEqual(stream.autoreset, auto) + + @patch('colorama.ansitowin32.winterm', None) + @patch('colorama.ansitowin32.winapi_test', lambda *_: True) + def testStripIsTrueOnWindows(self): + with osname('nt'): + mockStdout = Mock() + stream = AnsiToWin32(mockStdout) + self.assertTrue(stream.strip) + + def testStripIsFalseOffWindows(self): + with osname('posix'): + mockStdout = Mock(closed=False) + stream = AnsiToWin32(mockStdout) + self.assertFalse(stream.strip) + + def testWriteStripsAnsi(self): + mockStdout = Mock() + stream = AnsiToWin32(mockStdout) + stream.wrapped = Mock() + stream.write_and_convert = Mock() + stream.strip = True + + stream.write('abc') + + self.assertFalse(stream.wrapped.write.called) + self.assertEqual(stream.write_and_convert.call_args, (('abc',), {})) + + def testWriteDoesNotStripAnsi(self): + mockStdout = Mock() + stream = AnsiToWin32(mockStdout) + stream.wrapped = Mock() + stream.write_and_convert = Mock() + stream.strip = False + stream.convert = False + + stream.write('abc') + + self.assertFalse(stream.write_and_convert.called) + self.assertEqual(stream.wrapped.write.call_args, (('abc',), {})) + + def assert_autoresets(self, convert, autoreset=True): + stream = AnsiToWin32(Mock()) + stream.convert = convert + stream.reset_all = Mock() + stream.autoreset = autoreset + stream.winterm = Mock() + + stream.write('abc') + + self.assertEqual(stream.reset_all.called, autoreset) + + def testWriteAutoresets(self): + self.assert_autoresets(convert=True) + self.assert_autoresets(convert=False) + self.assert_autoresets(convert=True, autoreset=False) + self.assert_autoresets(convert=False, autoreset=False) + + def testWriteAndConvertWritesPlainText(self): + stream = AnsiToWin32(Mock()) + stream.write_and_convert( 'abc' ) + self.assertEqual( stream.wrapped.write.call_args, (('abc',), {}) ) + + def testWriteAndConvertStripsAllValidAnsi(self): + stream = AnsiToWin32(Mock()) + stream.call_win32 = Mock() + data = [ + 'abc\033[mdef', + 'abc\033[0mdef', + 'abc\033[2mdef', + 'abc\033[02mdef', + 'abc\033[002mdef', + 'abc\033[40mdef', + 'abc\033[040mdef', + 'abc\033[0;1mdef', + 'abc\033[40;50mdef', + 'abc\033[50;30;40mdef', + 'abc\033[Adef', + 'abc\033[0Gdef', + 'abc\033[1;20;128Hdef', + ] + for datum in data: + stream.wrapped.write.reset_mock() + stream.write_and_convert( datum ) + self.assertEqual( + [args[0] for args in stream.wrapped.write.call_args_list], + [ ('abc',), ('def',) ] + ) + + def testWriteAndConvertSkipsEmptySnippets(self): + stream = AnsiToWin32(Mock()) + stream.call_win32 = Mock() + stream.write_and_convert( '\033[40m\033[41m' ) + self.assertFalse( stream.wrapped.write.called ) + + def testWriteAndConvertCallsWin32WithParamsAndCommand(self): + stream = AnsiToWin32(Mock()) + stream.convert = True + stream.call_win32 = Mock() + stream.extract_params = Mock(return_value='params') + data = { + 'abc\033[adef': ('a', 'params'), + 'abc\033[;;bdef': ('b', 'params'), + 'abc\033[0cdef': ('c', 'params'), + 'abc\033[;;0;;Gdef': ('G', 'params'), + 'abc\033[1;20;128Hdef': ('H', 'params'), + } + for datum, expected in data.items(): + stream.call_win32.reset_mock() + stream.write_and_convert( datum ) + self.assertEqual( stream.call_win32.call_args[0], expected ) + + def test_reset_all_shouldnt_raise_on_closed_orig_stdout(self): + stream = StringIO() + converter = AnsiToWin32(stream) + stream.close() + + converter.reset_all() + + def test_wrap_shouldnt_raise_on_closed_orig_stdout(self): + stream = StringIO() + stream.close() + with \ + patch("colorama.ansitowin32.os.name", "nt"), \ + patch("colorama.ansitowin32.winapi_test", lambda: True): + converter = AnsiToWin32(stream) + self.assertTrue(converter.strip) + self.assertFalse(converter.convert) + + def test_wrap_shouldnt_raise_on_missing_closed_attr(self): + with \ + patch("colorama.ansitowin32.os.name", "nt"), \ + patch("colorama.ansitowin32.winapi_test", lambda: True): + converter = AnsiToWin32(object()) + self.assertTrue(converter.strip) + self.assertFalse(converter.convert) + + def testExtractParams(self): + stream = AnsiToWin32(Mock()) + data = { + '': (0,), + ';;': (0,), + '2': (2,), + ';;002;;': (2,), + '0;1': (0, 1), + ';;003;;456;;': (3, 456), + '11;22;33;44;55': (11, 22, 33, 44, 55), + } + for datum, expected in data.items(): + self.assertEqual(stream.extract_params('m', datum), expected) + + def testCallWin32UsesLookup(self): + listener = Mock() + stream = AnsiToWin32(listener) + stream.win32_calls = { + 1: (lambda *_, **__: listener(11),), + 2: (lambda *_, **__: listener(22),), + 3: (lambda *_, **__: listener(33),), + } + stream.call_win32('m', (3, 1, 99, 2)) + self.assertEqual( + [a[0][0] for a in listener.call_args_list], + [33, 11, 22] ) + + def test_osc_codes(self): + mockStdout = Mock() + stream = AnsiToWin32(mockStdout, convert=True) + with patch('colorama.ansitowin32.winterm') as winterm: + data = [ + '\033]0\x07', # missing arguments + '\033]0;foo\x08', # wrong OSC command + '\033]0;colorama_test_title\x07', # should work + '\033]1;colorama_test_title\x07', # wrong set command + '\033]2;colorama_test_title\x07', # should work + '\033]' + ';' * 64 + '\x08', # see issue #247 + ] + for code in data: + stream.write(code) + self.assertEqual(winterm.set_title.call_count, 2) + + def test_native_windows_ansi(self): + with ExitStack() as stack: + def p(a, b): + stack.enter_context(patch(a, b, create=True)) + # Pretend to be on Windows + p("colorama.ansitowin32.os.name", "nt") + p("colorama.ansitowin32.winapi_test", lambda: True) + p("colorama.win32.winapi_test", lambda: True) + p("colorama.winterm.win32.windll", "non-None") + p("colorama.winterm.get_osfhandle", lambda _: 1234) + + # Pretend that our mock stream has native ANSI support + p( + "colorama.winterm.win32.GetConsoleMode", + lambda _: ENABLE_VIRTUAL_TERMINAL_PROCESSING, + ) + SetConsoleMode = Mock() + p("colorama.winterm.win32.SetConsoleMode", SetConsoleMode) + + stdout = Mock() + stdout.closed = False + stdout.isatty.return_value = True + stdout.fileno.return_value = 1 + + # Our fake console says it has native vt support, so AnsiToWin32 should + # enable that support and do nothing else. + stream = AnsiToWin32(stdout) + SetConsoleMode.assert_called_with(1234, ENABLE_VIRTUAL_TERMINAL_PROCESSING) + self.assertFalse(stream.strip) + self.assertFalse(stream.convert) + self.assertFalse(stream.should_wrap()) + + # Now let's pretend we're on an old Windows console, that doesn't have + # native ANSI support. + p("colorama.winterm.win32.GetConsoleMode", lambda _: 0) + SetConsoleMode = Mock() + p("colorama.winterm.win32.SetConsoleMode", SetConsoleMode) + + stream = AnsiToWin32(stdout) + SetConsoleMode.assert_called_with(1234, ENABLE_VIRTUAL_TERMINAL_PROCESSING) + self.assertTrue(stream.strip) + self.assertTrue(stream.convert) + self.assertTrue(stream.should_wrap()) + + +if __name__ == '__main__': + main() diff --git a/venv/lib/python3.12/site-packages/colorama/tests/initialise_test.py b/venv/lib/python3.12/site-packages/colorama/tests/initialise_test.py new file mode 100644 index 00000000..89f9b075 --- /dev/null +++ b/venv/lib/python3.12/site-packages/colorama/tests/initialise_test.py @@ -0,0 +1,189 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +import sys +from unittest import TestCase, main, skipUnless + +try: + from unittest.mock import patch, Mock +except ImportError: + from mock import patch, Mock + +from ..ansitowin32 import StreamWrapper +from ..initialise import init, just_fix_windows_console, _wipe_internal_state_for_tests +from .utils import osname, replace_by + +orig_stdout = sys.stdout +orig_stderr = sys.stderr + + +class InitTest(TestCase): + + @skipUnless(sys.stdout.isatty(), "sys.stdout is not a tty") + def setUp(self): + # sanity check + self.assertNotWrapped() + + def tearDown(self): + _wipe_internal_state_for_tests() + sys.stdout = orig_stdout + sys.stderr = orig_stderr + + def assertWrapped(self): + self.assertIsNot(sys.stdout, orig_stdout, 'stdout should be wrapped') + self.assertIsNot(sys.stderr, orig_stderr, 'stderr should be wrapped') + self.assertTrue(isinstance(sys.stdout, StreamWrapper), + 'bad stdout wrapper') + self.assertTrue(isinstance(sys.stderr, StreamWrapper), + 'bad stderr wrapper') + + def assertNotWrapped(self): + self.assertIs(sys.stdout, orig_stdout, 'stdout should not be wrapped') + self.assertIs(sys.stderr, orig_stderr, 'stderr should not be wrapped') + + @patch('colorama.initialise.reset_all') + @patch('colorama.ansitowin32.winapi_test', lambda *_: True) + @patch('colorama.ansitowin32.enable_vt_processing', lambda *_: False) + def testInitWrapsOnWindows(self, _): + with osname("nt"): + init() + self.assertWrapped() + + @patch('colorama.initialise.reset_all') + @patch('colorama.ansitowin32.winapi_test', lambda *_: False) + def testInitDoesntWrapOnEmulatedWindows(self, _): + with osname("nt"): + init() + self.assertNotWrapped() + + def testInitDoesntWrapOnNonWindows(self): + with osname("posix"): + init() + self.assertNotWrapped() + + def testInitDoesntWrapIfNone(self): + with replace_by(None): + init() + # We can't use assertNotWrapped here because replace_by(None) + # changes stdout/stderr already. + self.assertIsNone(sys.stdout) + self.assertIsNone(sys.stderr) + + def testInitAutoresetOnWrapsOnAllPlatforms(self): + with osname("posix"): + init(autoreset=True) + self.assertWrapped() + + def testInitWrapOffDoesntWrapOnWindows(self): + with osname("nt"): + init(wrap=False) + self.assertNotWrapped() + + def testInitWrapOffIncompatibleWithAutoresetOn(self): + self.assertRaises(ValueError, lambda: init(autoreset=True, wrap=False)) + + @patch('colorama.win32.SetConsoleTextAttribute') + @patch('colorama.initialise.AnsiToWin32') + def testAutoResetPassedOn(self, mockATW32, _): + with osname("nt"): + init(autoreset=True) + self.assertEqual(len(mockATW32.call_args_list), 2) + self.assertEqual(mockATW32.call_args_list[1][1]['autoreset'], True) + self.assertEqual(mockATW32.call_args_list[0][1]['autoreset'], True) + + @patch('colorama.initialise.AnsiToWin32') + def testAutoResetChangeable(self, mockATW32): + with osname("nt"): + init() + + init(autoreset=True) + self.assertEqual(len(mockATW32.call_args_list), 4) + self.assertEqual(mockATW32.call_args_list[2][1]['autoreset'], True) + self.assertEqual(mockATW32.call_args_list[3][1]['autoreset'], True) + + init() + self.assertEqual(len(mockATW32.call_args_list), 6) + self.assertEqual( + mockATW32.call_args_list[4][1]['autoreset'], False) + self.assertEqual( + mockATW32.call_args_list[5][1]['autoreset'], False) + + + @patch('colorama.initialise.atexit.register') + def testAtexitRegisteredOnlyOnce(self, mockRegister): + init() + self.assertTrue(mockRegister.called) + mockRegister.reset_mock() + init() + self.assertFalse(mockRegister.called) + + +class JustFixWindowsConsoleTest(TestCase): + def _reset(self): + _wipe_internal_state_for_tests() + sys.stdout = orig_stdout + sys.stderr = orig_stderr + + def tearDown(self): + self._reset() + + @patch("colorama.ansitowin32.winapi_test", lambda: True) + def testJustFixWindowsConsole(self): + if sys.platform != "win32": + # just_fix_windows_console should be a no-op + just_fix_windows_console() + self.assertIs(sys.stdout, orig_stdout) + self.assertIs(sys.stderr, orig_stderr) + else: + def fake_std(): + # Emulate stdout=not a tty, stderr=tty + # to check that we handle both cases correctly + stdout = Mock() + stdout.closed = False + stdout.isatty.return_value = False + stdout.fileno.return_value = 1 + sys.stdout = stdout + + stderr = Mock() + stderr.closed = False + stderr.isatty.return_value = True + stderr.fileno.return_value = 2 + sys.stderr = stderr + + for native_ansi in [False, True]: + with patch( + 'colorama.ansitowin32.enable_vt_processing', + lambda *_: native_ansi + ): + self._reset() + fake_std() + + # Regular single-call test + prev_stdout = sys.stdout + prev_stderr = sys.stderr + just_fix_windows_console() + self.assertIs(sys.stdout, prev_stdout) + if native_ansi: + self.assertIs(sys.stderr, prev_stderr) + else: + self.assertIsNot(sys.stderr, prev_stderr) + + # second call without resetting is always a no-op + prev_stdout = sys.stdout + prev_stderr = sys.stderr + just_fix_windows_console() + self.assertIs(sys.stdout, prev_stdout) + self.assertIs(sys.stderr, prev_stderr) + + self._reset() + fake_std() + + # If init() runs first, just_fix_windows_console should be a no-op + init() + prev_stdout = sys.stdout + prev_stderr = sys.stderr + just_fix_windows_console() + self.assertIs(prev_stdout, sys.stdout) + self.assertIs(prev_stderr, sys.stderr) + + +if __name__ == '__main__': + main() diff --git a/venv/lib/python3.12/site-packages/colorama/tests/isatty_test.py b/venv/lib/python3.12/site-packages/colorama/tests/isatty_test.py new file mode 100644 index 00000000..0f84e4be --- /dev/null +++ b/venv/lib/python3.12/site-packages/colorama/tests/isatty_test.py @@ -0,0 +1,57 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +import sys +from unittest import TestCase, main + +from ..ansitowin32 import StreamWrapper, AnsiToWin32 +from .utils import pycharm, replace_by, replace_original_by, StreamTTY, StreamNonTTY + + +def is_a_tty(stream): + return StreamWrapper(stream, None).isatty() + +class IsattyTest(TestCase): + + def test_TTY(self): + tty = StreamTTY() + self.assertTrue(is_a_tty(tty)) + with pycharm(): + self.assertTrue(is_a_tty(tty)) + + def test_nonTTY(self): + non_tty = StreamNonTTY() + self.assertFalse(is_a_tty(non_tty)) + with pycharm(): + self.assertFalse(is_a_tty(non_tty)) + + def test_withPycharm(self): + with pycharm(): + self.assertTrue(is_a_tty(sys.stderr)) + self.assertTrue(is_a_tty(sys.stdout)) + + def test_withPycharmTTYOverride(self): + tty = StreamTTY() + with pycharm(), replace_by(tty): + self.assertTrue(is_a_tty(tty)) + + def test_withPycharmNonTTYOverride(self): + non_tty = StreamNonTTY() + with pycharm(), replace_by(non_tty): + self.assertFalse(is_a_tty(non_tty)) + + def test_withPycharmNoneOverride(self): + with pycharm(): + with replace_by(None), replace_original_by(None): + self.assertFalse(is_a_tty(None)) + self.assertFalse(is_a_tty(StreamNonTTY())) + self.assertTrue(is_a_tty(StreamTTY())) + + def test_withPycharmStreamWrapped(self): + with pycharm(): + self.assertTrue(AnsiToWin32(StreamTTY()).stream.isatty()) + self.assertFalse(AnsiToWin32(StreamNonTTY()).stream.isatty()) + self.assertTrue(AnsiToWin32(sys.stdout).stream.isatty()) + self.assertTrue(AnsiToWin32(sys.stderr).stream.isatty()) + + +if __name__ == '__main__': + main() diff --git a/venv/lib/python3.12/site-packages/colorama/tests/utils.py b/venv/lib/python3.12/site-packages/colorama/tests/utils.py new file mode 100644 index 00000000..472fafb4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/colorama/tests/utils.py @@ -0,0 +1,49 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +from contextlib import contextmanager +from io import StringIO +import sys +import os + + +class StreamTTY(StringIO): + def isatty(self): + return True + +class StreamNonTTY(StringIO): + def isatty(self): + return False + +@contextmanager +def osname(name): + orig = os.name + os.name = name + yield + os.name = orig + +@contextmanager +def replace_by(stream): + orig_stdout = sys.stdout + orig_stderr = sys.stderr + sys.stdout = stream + sys.stderr = stream + yield + sys.stdout = orig_stdout + sys.stderr = orig_stderr + +@contextmanager +def replace_original_by(stream): + orig_stdout = sys.__stdout__ + orig_stderr = sys.__stderr__ + sys.__stdout__ = stream + sys.__stderr__ = stream + yield + sys.__stdout__ = orig_stdout + sys.__stderr__ = orig_stderr + +@contextmanager +def pycharm(): + os.environ["PYCHARM_HOSTED"] = "1" + non_tty = StreamNonTTY() + with replace_by(non_tty), replace_original_by(non_tty): + yield + del os.environ["PYCHARM_HOSTED"] diff --git a/venv/lib/python3.12/site-packages/colorama/tests/winterm_test.py b/venv/lib/python3.12/site-packages/colorama/tests/winterm_test.py new file mode 100644 index 00000000..d0955f9e --- /dev/null +++ b/venv/lib/python3.12/site-packages/colorama/tests/winterm_test.py @@ -0,0 +1,131 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +import sys +from unittest import TestCase, main, skipUnless + +try: + from unittest.mock import Mock, patch +except ImportError: + from mock import Mock, patch + +from ..winterm import WinColor, WinStyle, WinTerm + + +class WinTermTest(TestCase): + + @patch('colorama.winterm.win32') + def testInit(self, mockWin32): + mockAttr = Mock() + mockAttr.wAttributes = 7 + 6 * 16 + 8 + mockWin32.GetConsoleScreenBufferInfo.return_value = mockAttr + term = WinTerm() + self.assertEqual(term._fore, 7) + self.assertEqual(term._back, 6) + self.assertEqual(term._style, 8) + + @skipUnless(sys.platform.startswith("win"), "requires Windows") + def testGetAttrs(self): + term = WinTerm() + + term._fore = 0 + term._back = 0 + term._style = 0 + self.assertEqual(term.get_attrs(), 0) + + term._fore = WinColor.YELLOW + self.assertEqual(term.get_attrs(), WinColor.YELLOW) + + term._back = WinColor.MAGENTA + self.assertEqual( + term.get_attrs(), + WinColor.YELLOW + WinColor.MAGENTA * 16) + + term._style = WinStyle.BRIGHT + self.assertEqual( + term.get_attrs(), + WinColor.YELLOW + WinColor.MAGENTA * 16 + WinStyle.BRIGHT) + + @patch('colorama.winterm.win32') + def testResetAll(self, mockWin32): + mockAttr = Mock() + mockAttr.wAttributes = 1 + 2 * 16 + 8 + mockWin32.GetConsoleScreenBufferInfo.return_value = mockAttr + term = WinTerm() + + term.set_console = Mock() + term._fore = -1 + term._back = -1 + term._style = -1 + + term.reset_all() + + self.assertEqual(term._fore, 1) + self.assertEqual(term._back, 2) + self.assertEqual(term._style, 8) + self.assertEqual(term.set_console.called, True) + + @skipUnless(sys.platform.startswith("win"), "requires Windows") + def testFore(self): + term = WinTerm() + term.set_console = Mock() + term._fore = 0 + + term.fore(5) + + self.assertEqual(term._fore, 5) + self.assertEqual(term.set_console.called, True) + + @skipUnless(sys.platform.startswith("win"), "requires Windows") + def testBack(self): + term = WinTerm() + term.set_console = Mock() + term._back = 0 + + term.back(5) + + self.assertEqual(term._back, 5) + self.assertEqual(term.set_console.called, True) + + @skipUnless(sys.platform.startswith("win"), "requires Windows") + def testStyle(self): + term = WinTerm() + term.set_console = Mock() + term._style = 0 + + term.style(22) + + self.assertEqual(term._style, 22) + self.assertEqual(term.set_console.called, True) + + @patch('colorama.winterm.win32') + def testSetConsole(self, mockWin32): + mockAttr = Mock() + mockAttr.wAttributes = 0 + mockWin32.GetConsoleScreenBufferInfo.return_value = mockAttr + term = WinTerm() + term.windll = Mock() + + term.set_console() + + self.assertEqual( + mockWin32.SetConsoleTextAttribute.call_args, + ((mockWin32.STDOUT, term.get_attrs()), {}) + ) + + @patch('colorama.winterm.win32') + def testSetConsoleOnStderr(self, mockWin32): + mockAttr = Mock() + mockAttr.wAttributes = 0 + mockWin32.GetConsoleScreenBufferInfo.return_value = mockAttr + term = WinTerm() + term.windll = Mock() + + term.set_console(on_stderr=True) + + self.assertEqual( + mockWin32.SetConsoleTextAttribute.call_args, + ((mockWin32.STDERR, term.get_attrs()), {}) + ) + + +if __name__ == '__main__': + main() diff --git a/venv/lib/python3.12/site-packages/colorama/win32.py b/venv/lib/python3.12/site-packages/colorama/win32.py new file mode 100644 index 00000000..841b0e27 --- /dev/null +++ b/venv/lib/python3.12/site-packages/colorama/win32.py @@ -0,0 +1,180 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. + +# from winbase.h +STDOUT = -11 +STDERR = -12 + +ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004 + +try: + import ctypes + from ctypes import LibraryLoader + windll = LibraryLoader(ctypes.WinDLL) + from ctypes import wintypes +except (AttributeError, ImportError): + windll = None + SetConsoleTextAttribute = lambda *_: None + winapi_test = lambda *_: None +else: + from ctypes import byref, Structure, c_char, POINTER + + COORD = wintypes._COORD + + class CONSOLE_SCREEN_BUFFER_INFO(Structure): + """struct in wincon.h.""" + _fields_ = [ + ("dwSize", COORD), + ("dwCursorPosition", COORD), + ("wAttributes", wintypes.WORD), + ("srWindow", wintypes.SMALL_RECT), + ("dwMaximumWindowSize", COORD), + ] + def __str__(self): + return '(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)' % ( + self.dwSize.Y, self.dwSize.X + , self.dwCursorPosition.Y, self.dwCursorPosition.X + , self.wAttributes + , self.srWindow.Top, self.srWindow.Left, self.srWindow.Bottom, self.srWindow.Right + , self.dwMaximumWindowSize.Y, self.dwMaximumWindowSize.X + ) + + _GetStdHandle = windll.kernel32.GetStdHandle + _GetStdHandle.argtypes = [ + wintypes.DWORD, + ] + _GetStdHandle.restype = wintypes.HANDLE + + _GetConsoleScreenBufferInfo = windll.kernel32.GetConsoleScreenBufferInfo + _GetConsoleScreenBufferInfo.argtypes = [ + wintypes.HANDLE, + POINTER(CONSOLE_SCREEN_BUFFER_INFO), + ] + _GetConsoleScreenBufferInfo.restype = wintypes.BOOL + + _SetConsoleTextAttribute = windll.kernel32.SetConsoleTextAttribute + _SetConsoleTextAttribute.argtypes = [ + wintypes.HANDLE, + wintypes.WORD, + ] + _SetConsoleTextAttribute.restype = wintypes.BOOL + + _SetConsoleCursorPosition = windll.kernel32.SetConsoleCursorPosition + _SetConsoleCursorPosition.argtypes = [ + wintypes.HANDLE, + COORD, + ] + _SetConsoleCursorPosition.restype = wintypes.BOOL + + _FillConsoleOutputCharacterA = windll.kernel32.FillConsoleOutputCharacterA + _FillConsoleOutputCharacterA.argtypes = [ + wintypes.HANDLE, + c_char, + wintypes.DWORD, + COORD, + POINTER(wintypes.DWORD), + ] + _FillConsoleOutputCharacterA.restype = wintypes.BOOL + + _FillConsoleOutputAttribute = windll.kernel32.FillConsoleOutputAttribute + _FillConsoleOutputAttribute.argtypes = [ + wintypes.HANDLE, + wintypes.WORD, + wintypes.DWORD, + COORD, + POINTER(wintypes.DWORD), + ] + _FillConsoleOutputAttribute.restype = wintypes.BOOL + + _SetConsoleTitleW = windll.kernel32.SetConsoleTitleW + _SetConsoleTitleW.argtypes = [ + wintypes.LPCWSTR + ] + _SetConsoleTitleW.restype = wintypes.BOOL + + _GetConsoleMode = windll.kernel32.GetConsoleMode + _GetConsoleMode.argtypes = [ + wintypes.HANDLE, + POINTER(wintypes.DWORD) + ] + _GetConsoleMode.restype = wintypes.BOOL + + _SetConsoleMode = windll.kernel32.SetConsoleMode + _SetConsoleMode.argtypes = [ + wintypes.HANDLE, + wintypes.DWORD + ] + _SetConsoleMode.restype = wintypes.BOOL + + def _winapi_test(handle): + csbi = CONSOLE_SCREEN_BUFFER_INFO() + success = _GetConsoleScreenBufferInfo( + handle, byref(csbi)) + return bool(success) + + def winapi_test(): + return any(_winapi_test(h) for h in + (_GetStdHandle(STDOUT), _GetStdHandle(STDERR))) + + def GetConsoleScreenBufferInfo(stream_id=STDOUT): + handle = _GetStdHandle(stream_id) + csbi = CONSOLE_SCREEN_BUFFER_INFO() + success = _GetConsoleScreenBufferInfo( + handle, byref(csbi)) + return csbi + + def SetConsoleTextAttribute(stream_id, attrs): + handle = _GetStdHandle(stream_id) + return _SetConsoleTextAttribute(handle, attrs) + + def SetConsoleCursorPosition(stream_id, position, adjust=True): + position = COORD(*position) + # If the position is out of range, do nothing. + if position.Y <= 0 or position.X <= 0: + return + # Adjust for Windows' SetConsoleCursorPosition: + # 1. being 0-based, while ANSI is 1-based. + # 2. expecting (x,y), while ANSI uses (y,x). + adjusted_position = COORD(position.Y - 1, position.X - 1) + if adjust: + # Adjust for viewport's scroll position + sr = GetConsoleScreenBufferInfo(STDOUT).srWindow + adjusted_position.Y += sr.Top + adjusted_position.X += sr.Left + # Resume normal processing + handle = _GetStdHandle(stream_id) + return _SetConsoleCursorPosition(handle, adjusted_position) + + def FillConsoleOutputCharacter(stream_id, char, length, start): + handle = _GetStdHandle(stream_id) + char = c_char(char.encode()) + length = wintypes.DWORD(length) + num_written = wintypes.DWORD(0) + # Note that this is hard-coded for ANSI (vs wide) bytes. + success = _FillConsoleOutputCharacterA( + handle, char, length, start, byref(num_written)) + return num_written.value + + def FillConsoleOutputAttribute(stream_id, attr, length, start): + ''' FillConsoleOutputAttribute( hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten )''' + handle = _GetStdHandle(stream_id) + attribute = wintypes.WORD(attr) + length = wintypes.DWORD(length) + num_written = wintypes.DWORD(0) + # Note that this is hard-coded for ANSI (vs wide) bytes. + return _FillConsoleOutputAttribute( + handle, attribute, length, start, byref(num_written)) + + def SetConsoleTitle(title): + return _SetConsoleTitleW(title) + + def GetConsoleMode(handle): + mode = wintypes.DWORD() + success = _GetConsoleMode(handle, byref(mode)) + if not success: + raise ctypes.WinError() + return mode.value + + def SetConsoleMode(handle, mode): + success = _SetConsoleMode(handle, mode) + if not success: + raise ctypes.WinError() diff --git a/venv/lib/python3.12/site-packages/colorama/winterm.py b/venv/lib/python3.12/site-packages/colorama/winterm.py new file mode 100644 index 00000000..aad867e8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/colorama/winterm.py @@ -0,0 +1,195 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +try: + from msvcrt import get_osfhandle +except ImportError: + def get_osfhandle(_): + raise OSError("This isn't windows!") + + +from . import win32 + +# from wincon.h +class WinColor(object): + BLACK = 0 + BLUE = 1 + GREEN = 2 + CYAN = 3 + RED = 4 + MAGENTA = 5 + YELLOW = 6 + GREY = 7 + +# from wincon.h +class WinStyle(object): + NORMAL = 0x00 # dim text, dim background + BRIGHT = 0x08 # bright text, dim background + BRIGHT_BACKGROUND = 0x80 # dim text, bright background + +class WinTerm(object): + + def __init__(self): + self._default = win32.GetConsoleScreenBufferInfo(win32.STDOUT).wAttributes + self.set_attrs(self._default) + self._default_fore = self._fore + self._default_back = self._back + self._default_style = self._style + # In order to emulate LIGHT_EX in windows, we borrow the BRIGHT style. + # So that LIGHT_EX colors and BRIGHT style do not clobber each other, + # we track them separately, since LIGHT_EX is overwritten by Fore/Back + # and BRIGHT is overwritten by Style codes. + self._light = 0 + + def get_attrs(self): + return self._fore + self._back * 16 + (self._style | self._light) + + def set_attrs(self, value): + self._fore = value & 7 + self._back = (value >> 4) & 7 + self._style = value & (WinStyle.BRIGHT | WinStyle.BRIGHT_BACKGROUND) + + def reset_all(self, on_stderr=None): + self.set_attrs(self._default) + self.set_console(attrs=self._default) + self._light = 0 + + def fore(self, fore=None, light=False, on_stderr=False): + if fore is None: + fore = self._default_fore + self._fore = fore + # Emulate LIGHT_EX with BRIGHT Style + if light: + self._light |= WinStyle.BRIGHT + else: + self._light &= ~WinStyle.BRIGHT + self.set_console(on_stderr=on_stderr) + + def back(self, back=None, light=False, on_stderr=False): + if back is None: + back = self._default_back + self._back = back + # Emulate LIGHT_EX with BRIGHT_BACKGROUND Style + if light: + self._light |= WinStyle.BRIGHT_BACKGROUND + else: + self._light &= ~WinStyle.BRIGHT_BACKGROUND + self.set_console(on_stderr=on_stderr) + + def style(self, style=None, on_stderr=False): + if style is None: + style = self._default_style + self._style = style + self.set_console(on_stderr=on_stderr) + + def set_console(self, attrs=None, on_stderr=False): + if attrs is None: + attrs = self.get_attrs() + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + win32.SetConsoleTextAttribute(handle, attrs) + + def get_position(self, handle): + position = win32.GetConsoleScreenBufferInfo(handle).dwCursorPosition + # Because Windows coordinates are 0-based, + # and win32.SetConsoleCursorPosition expects 1-based. + position.X += 1 + position.Y += 1 + return position + + def set_cursor_position(self, position=None, on_stderr=False): + if position is None: + # I'm not currently tracking the position, so there is no default. + # position = self.get_position() + return + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + win32.SetConsoleCursorPosition(handle, position) + + def cursor_adjust(self, x, y, on_stderr=False): + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + position = self.get_position(handle) + adjusted_position = (position.Y + y, position.X + x) + win32.SetConsoleCursorPosition(handle, adjusted_position, adjust=False) + + def erase_screen(self, mode=0, on_stderr=False): + # 0 should clear from the cursor to the end of the screen. + # 1 should clear from the cursor to the beginning of the screen. + # 2 should clear the entire screen, and move cursor to (1,1) + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + csbi = win32.GetConsoleScreenBufferInfo(handle) + # get the number of character cells in the current buffer + cells_in_screen = csbi.dwSize.X * csbi.dwSize.Y + # get number of character cells before current cursor position + cells_before_cursor = csbi.dwSize.X * csbi.dwCursorPosition.Y + csbi.dwCursorPosition.X + if mode == 0: + from_coord = csbi.dwCursorPosition + cells_to_erase = cells_in_screen - cells_before_cursor + elif mode == 1: + from_coord = win32.COORD(0, 0) + cells_to_erase = cells_before_cursor + elif mode == 2: + from_coord = win32.COORD(0, 0) + cells_to_erase = cells_in_screen + else: + # invalid mode + return + # fill the entire screen with blanks + win32.FillConsoleOutputCharacter(handle, ' ', cells_to_erase, from_coord) + # now set the buffer's attributes accordingly + win32.FillConsoleOutputAttribute(handle, self.get_attrs(), cells_to_erase, from_coord) + if mode == 2: + # put the cursor where needed + win32.SetConsoleCursorPosition(handle, (1, 1)) + + def erase_line(self, mode=0, on_stderr=False): + # 0 should clear from the cursor to the end of the line. + # 1 should clear from the cursor to the beginning of the line. + # 2 should clear the entire line. + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + csbi = win32.GetConsoleScreenBufferInfo(handle) + if mode == 0: + from_coord = csbi.dwCursorPosition + cells_to_erase = csbi.dwSize.X - csbi.dwCursorPosition.X + elif mode == 1: + from_coord = win32.COORD(0, csbi.dwCursorPosition.Y) + cells_to_erase = csbi.dwCursorPosition.X + elif mode == 2: + from_coord = win32.COORD(0, csbi.dwCursorPosition.Y) + cells_to_erase = csbi.dwSize.X + else: + # invalid mode + return + # fill the entire screen with blanks + win32.FillConsoleOutputCharacter(handle, ' ', cells_to_erase, from_coord) + # now set the buffer's attributes accordingly + win32.FillConsoleOutputAttribute(handle, self.get_attrs(), cells_to_erase, from_coord) + + def set_title(self, title): + win32.SetConsoleTitle(title) + + +def enable_vt_processing(fd): + if win32.windll is None or not win32.winapi_test(): + return False + + try: + handle = get_osfhandle(fd) + mode = win32.GetConsoleMode(handle) + win32.SetConsoleMode( + handle, + mode | win32.ENABLE_VIRTUAL_TERMINAL_PROCESSING, + ) + + mode = win32.GetConsoleMode(handle) + if mode & win32.ENABLE_VIRTUAL_TERMINAL_PROCESSING: + return True + # Can get TypeError in testsuite where 'fd' is a Mock() + except (OSError, TypeError): + return False diff --git a/venv/lib/python3.12/site-packages/dateutil/__init__.py b/venv/lib/python3.12/site-packages/dateutil/__init__.py new file mode 100644 index 00000000..a2c19c06 --- /dev/null +++ b/venv/lib/python3.12/site-packages/dateutil/__init__.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +import sys + +try: + from ._version import version as __version__ +except ImportError: + __version__ = 'unknown' + +__all__ = ['easter', 'parser', 'relativedelta', 'rrule', 'tz', + 'utils', 'zoneinfo'] + +def __getattr__(name): + import importlib + + if name in __all__: + return importlib.import_module("." + name, __name__) + raise AttributeError( + "module {!r} has not attribute {!r}".format(__name__, name) + ) + + +def __dir__(): + # __dir__ should include all the lazy-importable modules as well. + return [x for x in globals() if x not in sys.modules] + __all__ diff --git a/venv/lib/python3.12/site-packages/dateutil/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/dateutil/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 00000000..05af466f Binary files /dev/null and b/venv/lib/python3.12/site-packages/dateutil/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/dateutil/__pycache__/_common.cpython-312.pyc b/venv/lib/python3.12/site-packages/dateutil/__pycache__/_common.cpython-312.pyc new file mode 100644 index 00000000..1d50f089 Binary files /dev/null and b/venv/lib/python3.12/site-packages/dateutil/__pycache__/_common.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/dateutil/__pycache__/_version.cpython-312.pyc b/venv/lib/python3.12/site-packages/dateutil/__pycache__/_version.cpython-312.pyc new file mode 100644 index 00000000..8bc0f521 Binary files /dev/null and b/venv/lib/python3.12/site-packages/dateutil/__pycache__/_version.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/dateutil/__pycache__/easter.cpython-312.pyc b/venv/lib/python3.12/site-packages/dateutil/__pycache__/easter.cpython-312.pyc new file mode 100644 index 00000000..8c351aa7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/dateutil/__pycache__/easter.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/dateutil/__pycache__/relativedelta.cpython-312.pyc b/venv/lib/python3.12/site-packages/dateutil/__pycache__/relativedelta.cpython-312.pyc new file mode 100644 index 00000000..4e5fc3f7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/dateutil/__pycache__/relativedelta.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/dateutil/__pycache__/rrule.cpython-312.pyc b/venv/lib/python3.12/site-packages/dateutil/__pycache__/rrule.cpython-312.pyc new file mode 100644 index 00000000..1cc2f45d Binary files /dev/null and b/venv/lib/python3.12/site-packages/dateutil/__pycache__/rrule.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/dateutil/__pycache__/tzwin.cpython-312.pyc b/venv/lib/python3.12/site-packages/dateutil/__pycache__/tzwin.cpython-312.pyc new file mode 100644 index 00000000..98587e12 Binary files /dev/null and b/venv/lib/python3.12/site-packages/dateutil/__pycache__/tzwin.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/dateutil/__pycache__/utils.cpython-312.pyc b/venv/lib/python3.12/site-packages/dateutil/__pycache__/utils.cpython-312.pyc new file mode 100644 index 00000000..7e0f57d1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/dateutil/__pycache__/utils.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/dateutil/_common.py b/venv/lib/python3.12/site-packages/dateutil/_common.py new file mode 100644 index 00000000..4eb2659b --- /dev/null +++ b/venv/lib/python3.12/site-packages/dateutil/_common.py @@ -0,0 +1,43 @@ +""" +Common code used in multiple modules. +""" + + +class weekday(object): + __slots__ = ["weekday", "n"] + + def __init__(self, weekday, n=None): + self.weekday = weekday + self.n = n + + def __call__(self, n): + if n == self.n: + return self + else: + return self.__class__(self.weekday, n) + + def __eq__(self, other): + try: + if self.weekday != other.weekday or self.n != other.n: + return False + except AttributeError: + return False + return True + + def __hash__(self): + return hash(( + self.weekday, + self.n, + )) + + def __ne__(self, other): + return not (self == other) + + def __repr__(self): + s = ("MO", "TU", "WE", "TH", "FR", "SA", "SU")[self.weekday] + if not self.n: + return s + else: + return "%s(%+d)" % (s, self.n) + +# vim:ts=4:sw=4:et diff --git a/venv/lib/python3.12/site-packages/dateutil/_version.py b/venv/lib/python3.12/site-packages/dateutil/_version.py new file mode 100644 index 00000000..ddda9809 --- /dev/null +++ b/venv/lib/python3.12/site-packages/dateutil/_version.py @@ -0,0 +1,4 @@ +# file generated by setuptools_scm +# don't change, don't track in version control +__version__ = version = '2.9.0.post0' +__version_tuple__ = version_tuple = (2, 9, 0) diff --git a/venv/lib/python3.12/site-packages/dateutil/easter.py b/venv/lib/python3.12/site-packages/dateutil/easter.py new file mode 100644 index 00000000..f74d1f74 --- /dev/null +++ b/venv/lib/python3.12/site-packages/dateutil/easter.py @@ -0,0 +1,89 @@ +# -*- coding: utf-8 -*- +""" +This module offers a generic Easter computing method for any given year, using +Western, Orthodox or Julian algorithms. +""" + +import datetime + +__all__ = ["easter", "EASTER_JULIAN", "EASTER_ORTHODOX", "EASTER_WESTERN"] + +EASTER_JULIAN = 1 +EASTER_ORTHODOX = 2 +EASTER_WESTERN = 3 + + +def easter(year, method=EASTER_WESTERN): + """ + This method was ported from the work done by GM Arts, + on top of the algorithm by Claus Tondering, which was + based in part on the algorithm of Ouding (1940), as + quoted in "Explanatory Supplement to the Astronomical + Almanac", P. Kenneth Seidelmann, editor. + + This algorithm implements three different Easter + calculation methods: + + 1. Original calculation in Julian calendar, valid in + dates after 326 AD + 2. Original method, with date converted to Gregorian + calendar, valid in years 1583 to 4099 + 3. Revised method, in Gregorian calendar, valid in + years 1583 to 4099 as well + + These methods are represented by the constants: + + * ``EASTER_JULIAN = 1`` + * ``EASTER_ORTHODOX = 2`` + * ``EASTER_WESTERN = 3`` + + The default method is method 3. + + More about the algorithm may be found at: + + `GM Arts: Easter Algorithms `_ + + and + + `The Calendar FAQ: Easter `_ + + """ + + if not (1 <= method <= 3): + raise ValueError("invalid method") + + # g - Golden year - 1 + # c - Century + # h - (23 - Epact) mod 30 + # i - Number of days from March 21 to Paschal Full Moon + # j - Weekday for PFM (0=Sunday, etc) + # p - Number of days from March 21 to Sunday on or before PFM + # (-6 to 28 methods 1 & 3, to 56 for method 2) + # e - Extra days to add for method 2 (converting Julian + # date to Gregorian date) + + y = year + g = y % 19 + e = 0 + if method < 3: + # Old method + i = (19*g + 15) % 30 + j = (y + y//4 + i) % 7 + if method == 2: + # Extra dates to convert Julian to Gregorian date + e = 10 + if y > 1600: + e = e + y//100 - 16 - (y//100 - 16)//4 + else: + # New method + c = y//100 + h = (c - c//4 - (8*c + 13)//25 + 19*g + 15) % 30 + i = h - (h//28)*(1 - (h//28)*(29//(h + 1))*((21 - g)//11)) + j = (y + y//4 + i + 2 - c + c//4) % 7 + + # p can be from -6 to 56 corresponding to dates 22 March to 23 May + # (later dates apply to method 2, although 23 May never actually occurs) + p = i - j + e + d = 1 + (p + 27 + (p + 6)//40) % 31 + m = 3 + (p + 26)//30 + return datetime.date(int(y), int(m), int(d)) diff --git a/venv/lib/python3.12/site-packages/dateutil/parser/__init__.py b/venv/lib/python3.12/site-packages/dateutil/parser/__init__.py new file mode 100644 index 00000000..d174b0e4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/dateutil/parser/__init__.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- +from ._parser import parse, parser, parserinfo, ParserError +from ._parser import DEFAULTPARSER, DEFAULTTZPARSER +from ._parser import UnknownTimezoneWarning + +from ._parser import __doc__ + +from .isoparser import isoparser, isoparse + +__all__ = ['parse', 'parser', 'parserinfo', + 'isoparse', 'isoparser', + 'ParserError', + 'UnknownTimezoneWarning'] + + +### +# Deprecate portions of the private interface so that downstream code that +# is improperly relying on it is given *some* notice. + + +def __deprecated_private_func(f): + from functools import wraps + import warnings + + msg = ('{name} is a private function and may break without warning, ' + 'it will be moved and or renamed in future versions.') + msg = msg.format(name=f.__name__) + + @wraps(f) + def deprecated_func(*args, **kwargs): + warnings.warn(msg, DeprecationWarning) + return f(*args, **kwargs) + + return deprecated_func + +def __deprecate_private_class(c): + import warnings + + msg = ('{name} is a private class and may break without warning, ' + 'it will be moved and or renamed in future versions.') + msg = msg.format(name=c.__name__) + + class private_class(c): + __doc__ = c.__doc__ + + def __init__(self, *args, **kwargs): + warnings.warn(msg, DeprecationWarning) + super(private_class, self).__init__(*args, **kwargs) + + private_class.__name__ = c.__name__ + + return private_class + + +from ._parser import _timelex, _resultbase +from ._parser import _tzparser, _parsetz + +_timelex = __deprecate_private_class(_timelex) +_tzparser = __deprecate_private_class(_tzparser) +_resultbase = __deprecate_private_class(_resultbase) +_parsetz = __deprecated_private_func(_parsetz) diff --git a/venv/lib/python3.12/site-packages/dateutil/parser/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/dateutil/parser/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 00000000..5d9583a2 Binary files /dev/null and b/venv/lib/python3.12/site-packages/dateutil/parser/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/dateutil/parser/__pycache__/_parser.cpython-312.pyc b/venv/lib/python3.12/site-packages/dateutil/parser/__pycache__/_parser.cpython-312.pyc new file mode 100644 index 00000000..a751b5fa Binary files /dev/null and b/venv/lib/python3.12/site-packages/dateutil/parser/__pycache__/_parser.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/dateutil/parser/__pycache__/isoparser.cpython-312.pyc b/venv/lib/python3.12/site-packages/dateutil/parser/__pycache__/isoparser.cpython-312.pyc new file mode 100644 index 00000000..ef1135f1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/dateutil/parser/__pycache__/isoparser.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/dateutil/parser/_parser.py b/venv/lib/python3.12/site-packages/dateutil/parser/_parser.py new file mode 100644 index 00000000..37d1663b --- /dev/null +++ b/venv/lib/python3.12/site-packages/dateutil/parser/_parser.py @@ -0,0 +1,1613 @@ +# -*- coding: utf-8 -*- +""" +This module offers a generic date/time string parser which is able to parse +most known formats to represent a date and/or time. + +This module attempts to be forgiving with regards to unlikely input formats, +returning a datetime object even for dates which are ambiguous. If an element +of a date/time stamp is omitted, the following rules are applied: + +- If AM or PM is left unspecified, a 24-hour clock is assumed, however, an hour + on a 12-hour clock (``0 <= hour <= 12``) *must* be specified if AM or PM is + specified. +- If a time zone is omitted, a timezone-naive datetime is returned. + +If any other elements are missing, they are taken from the +:class:`datetime.datetime` object passed to the parameter ``default``. If this +results in a day number exceeding the valid number of days per month, the +value falls back to the end of the month. + +Additional resources about date/time string formats can be found below: + +- `A summary of the international standard date and time notation + `_ +- `W3C Date and Time Formats `_ +- `Time Formats (Planetary Rings Node) `_ +- `CPAN ParseDate module + `_ +- `Java SimpleDateFormat Class + `_ +""" +from __future__ import unicode_literals + +import datetime +import re +import string +import time +import warnings + +from calendar import monthrange +from io import StringIO + +import six +from six import integer_types, text_type + +from decimal import Decimal + +from warnings import warn + +from .. import relativedelta +from .. import tz + +__all__ = ["parse", "parserinfo", "ParserError"] + + +# TODO: pandas.core.tools.datetimes imports this explicitly. Might be worth +# making public and/or figuring out if there is something we can +# take off their plate. +class _timelex(object): + # Fractional seconds are sometimes split by a comma + _split_decimal = re.compile("([.,])") + + def __init__(self, instream): + if isinstance(instream, (bytes, bytearray)): + instream = instream.decode() + + if isinstance(instream, text_type): + instream = StringIO(instream) + elif getattr(instream, 'read', None) is None: + raise TypeError('Parser must be a string or character stream, not ' + '{itype}'.format(itype=instream.__class__.__name__)) + + self.instream = instream + self.charstack = [] + self.tokenstack = [] + self.eof = False + + def get_token(self): + """ + This function breaks the time string into lexical units (tokens), which + can be parsed by the parser. Lexical units are demarcated by changes in + the character set, so any continuous string of letters is considered + one unit, any continuous string of numbers is considered one unit. + + The main complication arises from the fact that dots ('.') can be used + both as separators (e.g. "Sep.20.2009") or decimal points (e.g. + "4:30:21.447"). As such, it is necessary to read the full context of + any dot-separated strings before breaking it into tokens; as such, this + function maintains a "token stack", for when the ambiguous context + demands that multiple tokens be parsed at once. + """ + if self.tokenstack: + return self.tokenstack.pop(0) + + seenletters = False + token = None + state = None + + while not self.eof: + # We only realize that we've reached the end of a token when we + # find a character that's not part of the current token - since + # that character may be part of the next token, it's stored in the + # charstack. + if self.charstack: + nextchar = self.charstack.pop(0) + else: + nextchar = self.instream.read(1) + while nextchar == '\x00': + nextchar = self.instream.read(1) + + if not nextchar: + self.eof = True + break + elif not state: + # First character of the token - determines if we're starting + # to parse a word, a number or something else. + token = nextchar + if self.isword(nextchar): + state = 'a' + elif self.isnum(nextchar): + state = '0' + elif self.isspace(nextchar): + token = ' ' + break # emit token + else: + break # emit token + elif state == 'a': + # If we've already started reading a word, we keep reading + # letters until we find something that's not part of a word. + seenletters = True + if self.isword(nextchar): + token += nextchar + elif nextchar == '.': + token += nextchar + state = 'a.' + else: + self.charstack.append(nextchar) + break # emit token + elif state == '0': + # If we've already started reading a number, we keep reading + # numbers until we find something that doesn't fit. + if self.isnum(nextchar): + token += nextchar + elif nextchar == '.' or (nextchar == ',' and len(token) >= 2): + token += nextchar + state = '0.' + else: + self.charstack.append(nextchar) + break # emit token + elif state == 'a.': + # If we've seen some letters and a dot separator, continue + # parsing, and the tokens will be broken up later. + seenletters = True + if nextchar == '.' or self.isword(nextchar): + token += nextchar + elif self.isnum(nextchar) and token[-1] == '.': + token += nextchar + state = '0.' + else: + self.charstack.append(nextchar) + break # emit token + elif state == '0.': + # If we've seen at least one dot separator, keep going, we'll + # break up the tokens later. + if nextchar == '.' or self.isnum(nextchar): + token += nextchar + elif self.isword(nextchar) and token[-1] == '.': + token += nextchar + state = 'a.' + else: + self.charstack.append(nextchar) + break # emit token + + if (state in ('a.', '0.') and (seenletters or token.count('.') > 1 or + token[-1] in '.,')): + l = self._split_decimal.split(token) + token = l[0] + for tok in l[1:]: + if tok: + self.tokenstack.append(tok) + + if state == '0.' and token.count('.') == 0: + token = token.replace(',', '.') + + return token + + def __iter__(self): + return self + + def __next__(self): + token = self.get_token() + if token is None: + raise StopIteration + + return token + + def next(self): + return self.__next__() # Python 2.x support + + @classmethod + def split(cls, s): + return list(cls(s)) + + @classmethod + def isword(cls, nextchar): + """ Whether or not the next character is part of a word """ + return nextchar.isalpha() + + @classmethod + def isnum(cls, nextchar): + """ Whether the next character is part of a number """ + return nextchar.isdigit() + + @classmethod + def isspace(cls, nextchar): + """ Whether the next character is whitespace """ + return nextchar.isspace() + + +class _resultbase(object): + + def __init__(self): + for attr in self.__slots__: + setattr(self, attr, None) + + def _repr(self, classname): + l = [] + for attr in self.__slots__: + value = getattr(self, attr) + if value is not None: + l.append("%s=%s" % (attr, repr(value))) + return "%s(%s)" % (classname, ", ".join(l)) + + def __len__(self): + return (sum(getattr(self, attr) is not None + for attr in self.__slots__)) + + def __repr__(self): + return self._repr(self.__class__.__name__) + + +class parserinfo(object): + """ + Class which handles what inputs are accepted. Subclass this to customize + the language and acceptable values for each parameter. + + :param dayfirst: + Whether to interpret the first value in an ambiguous 3-integer date + (e.g. 01/05/09) as the day (``True``) or month (``False``). If + ``yearfirst`` is set to ``True``, this distinguishes between YDM + and YMD. Default is ``False``. + + :param yearfirst: + Whether to interpret the first value in an ambiguous 3-integer date + (e.g. 01/05/09) as the year. If ``True``, the first number is taken + to be the year, otherwise the last number is taken to be the year. + Default is ``False``. + """ + + # m from a.m/p.m, t from ISO T separator + JUMP = [" ", ".", ",", ";", "-", "/", "'", + "at", "on", "and", "ad", "m", "t", "of", + "st", "nd", "rd", "th"] + + WEEKDAYS = [("Mon", "Monday"), + ("Tue", "Tuesday"), # TODO: "Tues" + ("Wed", "Wednesday"), + ("Thu", "Thursday"), # TODO: "Thurs" + ("Fri", "Friday"), + ("Sat", "Saturday"), + ("Sun", "Sunday")] + MONTHS = [("Jan", "January"), + ("Feb", "February"), # TODO: "Febr" + ("Mar", "March"), + ("Apr", "April"), + ("May", "May"), + ("Jun", "June"), + ("Jul", "July"), + ("Aug", "August"), + ("Sep", "Sept", "September"), + ("Oct", "October"), + ("Nov", "November"), + ("Dec", "December")] + HMS = [("h", "hour", "hours"), + ("m", "minute", "minutes"), + ("s", "second", "seconds")] + AMPM = [("am", "a"), + ("pm", "p")] + UTCZONE = ["UTC", "GMT", "Z", "z"] + PERTAIN = ["of"] + TZOFFSET = {} + # TODO: ERA = ["AD", "BC", "CE", "BCE", "Stardate", + # "Anno Domini", "Year of Our Lord"] + + def __init__(self, dayfirst=False, yearfirst=False): + self._jump = self._convert(self.JUMP) + self._weekdays = self._convert(self.WEEKDAYS) + self._months = self._convert(self.MONTHS) + self._hms = self._convert(self.HMS) + self._ampm = self._convert(self.AMPM) + self._utczone = self._convert(self.UTCZONE) + self._pertain = self._convert(self.PERTAIN) + + self.dayfirst = dayfirst + self.yearfirst = yearfirst + + self._year = time.localtime().tm_year + self._century = self._year // 100 * 100 + + def _convert(self, lst): + dct = {} + for i, v in enumerate(lst): + if isinstance(v, tuple): + for v in v: + dct[v.lower()] = i + else: + dct[v.lower()] = i + return dct + + def jump(self, name): + return name.lower() in self._jump + + def weekday(self, name): + try: + return self._weekdays[name.lower()] + except KeyError: + pass + return None + + def month(self, name): + try: + return self._months[name.lower()] + 1 + except KeyError: + pass + return None + + def hms(self, name): + try: + return self._hms[name.lower()] + except KeyError: + return None + + def ampm(self, name): + try: + return self._ampm[name.lower()] + except KeyError: + return None + + def pertain(self, name): + return name.lower() in self._pertain + + def utczone(self, name): + return name.lower() in self._utczone + + def tzoffset(self, name): + if name in self._utczone: + return 0 + + return self.TZOFFSET.get(name) + + def convertyear(self, year, century_specified=False): + """ + Converts two-digit years to year within [-50, 49] + range of self._year (current local time) + """ + + # Function contract is that the year is always positive + assert year >= 0 + + if year < 100 and not century_specified: + # assume current century to start + year += self._century + + if year >= self._year + 50: # if too far in future + year -= 100 + elif year < self._year - 50: # if too far in past + year += 100 + + return year + + def validate(self, res): + # move to info + if res.year is not None: + res.year = self.convertyear(res.year, res.century_specified) + + if ((res.tzoffset == 0 and not res.tzname) or + (res.tzname == 'Z' or res.tzname == 'z')): + res.tzname = "UTC" + res.tzoffset = 0 + elif res.tzoffset != 0 and res.tzname and self.utczone(res.tzname): + res.tzoffset = 0 + return True + + +class _ymd(list): + def __init__(self, *args, **kwargs): + super(self.__class__, self).__init__(*args, **kwargs) + self.century_specified = False + self.dstridx = None + self.mstridx = None + self.ystridx = None + + @property + def has_year(self): + return self.ystridx is not None + + @property + def has_month(self): + return self.mstridx is not None + + @property + def has_day(self): + return self.dstridx is not None + + def could_be_day(self, value): + if self.has_day: + return False + elif not self.has_month: + return 1 <= value <= 31 + elif not self.has_year: + # Be permissive, assume leap year + month = self[self.mstridx] + return 1 <= value <= monthrange(2000, month)[1] + else: + month = self[self.mstridx] + year = self[self.ystridx] + return 1 <= value <= monthrange(year, month)[1] + + def append(self, val, label=None): + if hasattr(val, '__len__'): + if val.isdigit() and len(val) > 2: + self.century_specified = True + if label not in [None, 'Y']: # pragma: no cover + raise ValueError(label) + label = 'Y' + elif val > 100: + self.century_specified = True + if label not in [None, 'Y']: # pragma: no cover + raise ValueError(label) + label = 'Y' + + super(self.__class__, self).append(int(val)) + + if label == 'M': + if self.has_month: + raise ValueError('Month is already set') + self.mstridx = len(self) - 1 + elif label == 'D': + if self.has_day: + raise ValueError('Day is already set') + self.dstridx = len(self) - 1 + elif label == 'Y': + if self.has_year: + raise ValueError('Year is already set') + self.ystridx = len(self) - 1 + + def _resolve_from_stridxs(self, strids): + """ + Try to resolve the identities of year/month/day elements using + ystridx, mstridx, and dstridx, if enough of these are specified. + """ + if len(self) == 3 and len(strids) == 2: + # we can back out the remaining stridx value + missing = [x for x in range(3) if x not in strids.values()] + key = [x for x in ['y', 'm', 'd'] if x not in strids] + assert len(missing) == len(key) == 1 + key = key[0] + val = missing[0] + strids[key] = val + + assert len(self) == len(strids) # otherwise this should not be called + out = {key: self[strids[key]] for key in strids} + return (out.get('y'), out.get('m'), out.get('d')) + + def resolve_ymd(self, yearfirst, dayfirst): + len_ymd = len(self) + year, month, day = (None, None, None) + + strids = (('y', self.ystridx), + ('m', self.mstridx), + ('d', self.dstridx)) + + strids = {key: val for key, val in strids if val is not None} + if (len(self) == len(strids) > 0 or + (len(self) == 3 and len(strids) == 2)): + return self._resolve_from_stridxs(strids) + + mstridx = self.mstridx + + if len_ymd > 3: + raise ValueError("More than three YMD values") + elif len_ymd == 1 or (mstridx is not None and len_ymd == 2): + # One member, or two members with a month string + if mstridx is not None: + month = self[mstridx] + # since mstridx is 0 or 1, self[mstridx-1] always + # looks up the other element + other = self[mstridx - 1] + else: + other = self[0] + + if len_ymd > 1 or mstridx is None: + if other > 31: + year = other + else: + day = other + + elif len_ymd == 2: + # Two members with numbers + if self[0] > 31: + # 99-01 + year, month = self + elif self[1] > 31: + # 01-99 + month, year = self + elif dayfirst and self[1] <= 12: + # 13-01 + day, month = self + else: + # 01-13 + month, day = self + + elif len_ymd == 3: + # Three members + if mstridx == 0: + if self[1] > 31: + # Apr-2003-25 + month, year, day = self + else: + month, day, year = self + elif mstridx == 1: + if self[0] > 31 or (yearfirst and self[2] <= 31): + # 99-Jan-01 + year, month, day = self + else: + # 01-Jan-01 + # Give precedence to day-first, since + # two-digit years is usually hand-written. + day, month, year = self + + elif mstridx == 2: + # WTF!? + if self[1] > 31: + # 01-99-Jan + day, year, month = self + else: + # 99-01-Jan + year, day, month = self + + else: + if (self[0] > 31 or + self.ystridx == 0 or + (yearfirst and self[1] <= 12 and self[2] <= 31)): + # 99-01-01 + if dayfirst and self[2] <= 12: + year, day, month = self + else: + year, month, day = self + elif self[0] > 12 or (dayfirst and self[1] <= 12): + # 13-01-01 + day, month, year = self + else: + # 01-13-01 + month, day, year = self + + return year, month, day + + +class parser(object): + def __init__(self, info=None): + self.info = info or parserinfo() + + def parse(self, timestr, default=None, + ignoretz=False, tzinfos=None, **kwargs): + """ + Parse the date/time string into a :class:`datetime.datetime` object. + + :param timestr: + Any date/time string using the supported formats. + + :param default: + The default datetime object, if this is a datetime object and not + ``None``, elements specified in ``timestr`` replace elements in the + default object. + + :param ignoretz: + If set ``True``, time zones in parsed strings are ignored and a + naive :class:`datetime.datetime` object is returned. + + :param tzinfos: + Additional time zone names / aliases which may be present in the + string. This argument maps time zone names (and optionally offsets + from those time zones) to time zones. This parameter can be a + dictionary with timezone aliases mapping time zone names to time + zones or a function taking two parameters (``tzname`` and + ``tzoffset``) and returning a time zone. + + The timezones to which the names are mapped can be an integer + offset from UTC in seconds or a :class:`tzinfo` object. + + .. doctest:: + :options: +NORMALIZE_WHITESPACE + + >>> from dateutil.parser import parse + >>> from dateutil.tz import gettz + >>> tzinfos = {"BRST": -7200, "CST": gettz("America/Chicago")} + >>> parse("2012-01-19 17:21:00 BRST", tzinfos=tzinfos) + datetime.datetime(2012, 1, 19, 17, 21, tzinfo=tzoffset(u'BRST', -7200)) + >>> parse("2012-01-19 17:21:00 CST", tzinfos=tzinfos) + datetime.datetime(2012, 1, 19, 17, 21, + tzinfo=tzfile('/usr/share/zoneinfo/America/Chicago')) + + This parameter is ignored if ``ignoretz`` is set. + + :param \\*\\*kwargs: + Keyword arguments as passed to ``_parse()``. + + :return: + Returns a :class:`datetime.datetime` object or, if the + ``fuzzy_with_tokens`` option is ``True``, returns a tuple, the + first element being a :class:`datetime.datetime` object, the second + a tuple containing the fuzzy tokens. + + :raises ParserError: + Raised for invalid or unknown string format, if the provided + :class:`tzinfo` is not in a valid format, or if an invalid date + would be created. + + :raises TypeError: + Raised for non-string or character stream input. + + :raises OverflowError: + Raised if the parsed date exceeds the largest valid C integer on + your system. + """ + + if default is None: + default = datetime.datetime.now().replace(hour=0, minute=0, + second=0, microsecond=0) + + res, skipped_tokens = self._parse(timestr, **kwargs) + + if res is None: + raise ParserError("Unknown string format: %s", timestr) + + if len(res) == 0: + raise ParserError("String does not contain a date: %s", timestr) + + try: + ret = self._build_naive(res, default) + except ValueError as e: + six.raise_from(ParserError(str(e) + ": %s", timestr), e) + + if not ignoretz: + ret = self._build_tzaware(ret, res, tzinfos) + + if kwargs.get('fuzzy_with_tokens', False): + return ret, skipped_tokens + else: + return ret + + class _result(_resultbase): + __slots__ = ["year", "month", "day", "weekday", + "hour", "minute", "second", "microsecond", + "tzname", "tzoffset", "ampm","any_unused_tokens"] + + def _parse(self, timestr, dayfirst=None, yearfirst=None, fuzzy=False, + fuzzy_with_tokens=False): + """ + Private method which performs the heavy lifting of parsing, called from + ``parse()``, which passes on its ``kwargs`` to this function. + + :param timestr: + The string to parse. + + :param dayfirst: + Whether to interpret the first value in an ambiguous 3-integer date + (e.g. 01/05/09) as the day (``True``) or month (``False``). If + ``yearfirst`` is set to ``True``, this distinguishes between YDM + and YMD. If set to ``None``, this value is retrieved from the + current :class:`parserinfo` object (which itself defaults to + ``False``). + + :param yearfirst: + Whether to interpret the first value in an ambiguous 3-integer date + (e.g. 01/05/09) as the year. If ``True``, the first number is taken + to be the year, otherwise the last number is taken to be the year. + If this is set to ``None``, the value is retrieved from the current + :class:`parserinfo` object (which itself defaults to ``False``). + + :param fuzzy: + Whether to allow fuzzy parsing, allowing for string like "Today is + January 1, 2047 at 8:21:00AM". + + :param fuzzy_with_tokens: + If ``True``, ``fuzzy`` is automatically set to True, and the parser + will return a tuple where the first element is the parsed + :class:`datetime.datetime` datetimestamp and the second element is + a tuple containing the portions of the string which were ignored: + + .. doctest:: + + >>> from dateutil.parser import parse + >>> parse("Today is January 1, 2047 at 8:21:00AM", fuzzy_with_tokens=True) + (datetime.datetime(2047, 1, 1, 8, 21), (u'Today is ', u' ', u'at ')) + + """ + if fuzzy_with_tokens: + fuzzy = True + + info = self.info + + if dayfirst is None: + dayfirst = info.dayfirst + + if yearfirst is None: + yearfirst = info.yearfirst + + res = self._result() + l = _timelex.split(timestr) # Splits the timestr into tokens + + skipped_idxs = [] + + # year/month/day list + ymd = _ymd() + + len_l = len(l) + i = 0 + try: + while i < len_l: + + # Check if it's a number + value_repr = l[i] + try: + value = float(value_repr) + except ValueError: + value = None + + if value is not None: + # Numeric token + i = self._parse_numeric_token(l, i, info, ymd, res, fuzzy) + + # Check weekday + elif info.weekday(l[i]) is not None: + value = info.weekday(l[i]) + res.weekday = value + + # Check month name + elif info.month(l[i]) is not None: + value = info.month(l[i]) + ymd.append(value, 'M') + + if i + 1 < len_l: + if l[i + 1] in ('-', '/'): + # Jan-01[-99] + sep = l[i + 1] + ymd.append(l[i + 2]) + + if i + 3 < len_l and l[i + 3] == sep: + # Jan-01-99 + ymd.append(l[i + 4]) + i += 2 + + i += 2 + + elif (i + 4 < len_l and l[i + 1] == l[i + 3] == ' ' and + info.pertain(l[i + 2])): + # Jan of 01 + # In this case, 01 is clearly year + if l[i + 4].isdigit(): + # Convert it here to become unambiguous + value = int(l[i + 4]) + year = str(info.convertyear(value)) + ymd.append(year, 'Y') + else: + # Wrong guess + pass + # TODO: not hit in tests + i += 4 + + # Check am/pm + elif info.ampm(l[i]) is not None: + value = info.ampm(l[i]) + val_is_ampm = self._ampm_valid(res.hour, res.ampm, fuzzy) + + if val_is_ampm: + res.hour = self._adjust_ampm(res.hour, value) + res.ampm = value + + elif fuzzy: + skipped_idxs.append(i) + + # Check for a timezone name + elif self._could_be_tzname(res.hour, res.tzname, res.tzoffset, l[i]): + res.tzname = l[i] + res.tzoffset = info.tzoffset(res.tzname) + + # Check for something like GMT+3, or BRST+3. Notice + # that it doesn't mean "I am 3 hours after GMT", but + # "my time +3 is GMT". If found, we reverse the + # logic so that timezone parsing code will get it + # right. + if i + 1 < len_l and l[i + 1] in ('+', '-'): + l[i + 1] = ('+', '-')[l[i + 1] == '+'] + res.tzoffset = None + if info.utczone(res.tzname): + # With something like GMT+3, the timezone + # is *not* GMT. + res.tzname = None + + # Check for a numbered timezone + elif res.hour is not None and l[i] in ('+', '-'): + signal = (-1, 1)[l[i] == '+'] + len_li = len(l[i + 1]) + + # TODO: check that l[i + 1] is integer? + if len_li == 4: + # -0300 + hour_offset = int(l[i + 1][:2]) + min_offset = int(l[i + 1][2:]) + elif i + 2 < len_l and l[i + 2] == ':': + # -03:00 + hour_offset = int(l[i + 1]) + min_offset = int(l[i + 3]) # TODO: Check that l[i+3] is minute-like? + i += 2 + elif len_li <= 2: + # -[0]3 + hour_offset = int(l[i + 1][:2]) + min_offset = 0 + else: + raise ValueError(timestr) + + res.tzoffset = signal * (hour_offset * 3600 + min_offset * 60) + + # Look for a timezone name between parenthesis + if (i + 5 < len_l and + info.jump(l[i + 2]) and l[i + 3] == '(' and + l[i + 5] == ')' and + 3 <= len(l[i + 4]) and + self._could_be_tzname(res.hour, res.tzname, + None, l[i + 4])): + # -0300 (BRST) + res.tzname = l[i + 4] + i += 4 + + i += 1 + + # Check jumps + elif not (info.jump(l[i]) or fuzzy): + raise ValueError(timestr) + + else: + skipped_idxs.append(i) + i += 1 + + # Process year/month/day + year, month, day = ymd.resolve_ymd(yearfirst, dayfirst) + + res.century_specified = ymd.century_specified + res.year = year + res.month = month + res.day = day + + except (IndexError, ValueError): + return None, None + + if not info.validate(res): + return None, None + + if fuzzy_with_tokens: + skipped_tokens = self._recombine_skipped(l, skipped_idxs) + return res, tuple(skipped_tokens) + else: + return res, None + + def _parse_numeric_token(self, tokens, idx, info, ymd, res, fuzzy): + # Token is a number + value_repr = tokens[idx] + try: + value = self._to_decimal(value_repr) + except Exception as e: + six.raise_from(ValueError('Unknown numeric token'), e) + + len_li = len(value_repr) + + len_l = len(tokens) + + if (len(ymd) == 3 and len_li in (2, 4) and + res.hour is None and + (idx + 1 >= len_l or + (tokens[idx + 1] != ':' and + info.hms(tokens[idx + 1]) is None))): + # 19990101T23[59] + s = tokens[idx] + res.hour = int(s[:2]) + + if len_li == 4: + res.minute = int(s[2:]) + + elif len_li == 6 or (len_li > 6 and tokens[idx].find('.') == 6): + # YYMMDD or HHMMSS[.ss] + s = tokens[idx] + + if not ymd and '.' not in tokens[idx]: + ymd.append(s[:2]) + ymd.append(s[2:4]) + ymd.append(s[4:]) + else: + # 19990101T235959[.59] + + # TODO: Check if res attributes already set. + res.hour = int(s[:2]) + res.minute = int(s[2:4]) + res.second, res.microsecond = self._parsems(s[4:]) + + elif len_li in (8, 12, 14): + # YYYYMMDD + s = tokens[idx] + ymd.append(s[:4], 'Y') + ymd.append(s[4:6]) + ymd.append(s[6:8]) + + if len_li > 8: + res.hour = int(s[8:10]) + res.minute = int(s[10:12]) + + if len_li > 12: + res.second = int(s[12:]) + + elif self._find_hms_idx(idx, tokens, info, allow_jump=True) is not None: + # HH[ ]h or MM[ ]m or SS[.ss][ ]s + hms_idx = self._find_hms_idx(idx, tokens, info, allow_jump=True) + (idx, hms) = self._parse_hms(idx, tokens, info, hms_idx) + if hms is not None: + # TODO: checking that hour/minute/second are not + # already set? + self._assign_hms(res, value_repr, hms) + + elif idx + 2 < len_l and tokens[idx + 1] == ':': + # HH:MM[:SS[.ss]] + res.hour = int(value) + value = self._to_decimal(tokens[idx + 2]) # TODO: try/except for this? + (res.minute, res.second) = self._parse_min_sec(value) + + if idx + 4 < len_l and tokens[idx + 3] == ':': + res.second, res.microsecond = self._parsems(tokens[idx + 4]) + + idx += 2 + + idx += 2 + + elif idx + 1 < len_l and tokens[idx + 1] in ('-', '/', '.'): + sep = tokens[idx + 1] + ymd.append(value_repr) + + if idx + 2 < len_l and not info.jump(tokens[idx + 2]): + if tokens[idx + 2].isdigit(): + # 01-01[-01] + ymd.append(tokens[idx + 2]) + else: + # 01-Jan[-01] + value = info.month(tokens[idx + 2]) + + if value is not None: + ymd.append(value, 'M') + else: + raise ValueError() + + if idx + 3 < len_l and tokens[idx + 3] == sep: + # We have three members + value = info.month(tokens[idx + 4]) + + if value is not None: + ymd.append(value, 'M') + else: + ymd.append(tokens[idx + 4]) + idx += 2 + + idx += 1 + idx += 1 + + elif idx + 1 >= len_l or info.jump(tokens[idx + 1]): + if idx + 2 < len_l and info.ampm(tokens[idx + 2]) is not None: + # 12 am + hour = int(value) + res.hour = self._adjust_ampm(hour, info.ampm(tokens[idx + 2])) + idx += 1 + else: + # Year, month or day + ymd.append(value) + idx += 1 + + elif info.ampm(tokens[idx + 1]) is not None and (0 <= value < 24): + # 12am + hour = int(value) + res.hour = self._adjust_ampm(hour, info.ampm(tokens[idx + 1])) + idx += 1 + + elif ymd.could_be_day(value): + ymd.append(value) + + elif not fuzzy: + raise ValueError() + + return idx + + def _find_hms_idx(self, idx, tokens, info, allow_jump): + len_l = len(tokens) + + if idx+1 < len_l and info.hms(tokens[idx+1]) is not None: + # There is an "h", "m", or "s" label following this token. We take + # assign the upcoming label to the current token. + # e.g. the "12" in 12h" + hms_idx = idx + 1 + + elif (allow_jump and idx+2 < len_l and tokens[idx+1] == ' ' and + info.hms(tokens[idx+2]) is not None): + # There is a space and then an "h", "m", or "s" label. + # e.g. the "12" in "12 h" + hms_idx = idx + 2 + + elif idx > 0 and info.hms(tokens[idx-1]) is not None: + # There is a "h", "m", or "s" preceding this token. Since neither + # of the previous cases was hit, there is no label following this + # token, so we use the previous label. + # e.g. the "04" in "12h04" + hms_idx = idx-1 + + elif (1 < idx == len_l-1 and tokens[idx-1] == ' ' and + info.hms(tokens[idx-2]) is not None): + # If we are looking at the final token, we allow for a + # backward-looking check to skip over a space. + # TODO: Are we sure this is the right condition here? + hms_idx = idx - 2 + + else: + hms_idx = None + + return hms_idx + + def _assign_hms(self, res, value_repr, hms): + # See GH issue #427, fixing float rounding + value = self._to_decimal(value_repr) + + if hms == 0: + # Hour + res.hour = int(value) + if value % 1: + res.minute = int(60*(value % 1)) + + elif hms == 1: + (res.minute, res.second) = self._parse_min_sec(value) + + elif hms == 2: + (res.second, res.microsecond) = self._parsems(value_repr) + + def _could_be_tzname(self, hour, tzname, tzoffset, token): + return (hour is not None and + tzname is None and + tzoffset is None and + len(token) <= 5 and + (all(x in string.ascii_uppercase for x in token) + or token in self.info.UTCZONE)) + + def _ampm_valid(self, hour, ampm, fuzzy): + """ + For fuzzy parsing, 'a' or 'am' (both valid English words) + may erroneously trigger the AM/PM flag. Deal with that + here. + """ + val_is_ampm = True + + # If there's already an AM/PM flag, this one isn't one. + if fuzzy and ampm is not None: + val_is_ampm = False + + # If AM/PM is found and hour is not, raise a ValueError + if hour is None: + if fuzzy: + val_is_ampm = False + else: + raise ValueError('No hour specified with AM or PM flag.') + elif not 0 <= hour <= 12: + # If AM/PM is found, it's a 12 hour clock, so raise + # an error for invalid range + if fuzzy: + val_is_ampm = False + else: + raise ValueError('Invalid hour specified for 12-hour clock.') + + return val_is_ampm + + def _adjust_ampm(self, hour, ampm): + if hour < 12 and ampm == 1: + hour += 12 + elif hour == 12 and ampm == 0: + hour = 0 + return hour + + def _parse_min_sec(self, value): + # TODO: Every usage of this function sets res.second to the return + # value. Are there any cases where second will be returned as None and + # we *don't* want to set res.second = None? + minute = int(value) + second = None + + sec_remainder = value % 1 + if sec_remainder: + second = int(60 * sec_remainder) + return (minute, second) + + def _parse_hms(self, idx, tokens, info, hms_idx): + # TODO: Is this going to admit a lot of false-positives for when we + # just happen to have digits and "h", "m" or "s" characters in non-date + # text? I guess hex hashes won't have that problem, but there's plenty + # of random junk out there. + if hms_idx is None: + hms = None + new_idx = idx + elif hms_idx > idx: + hms = info.hms(tokens[hms_idx]) + new_idx = hms_idx + else: + # Looking backwards, increment one. + hms = info.hms(tokens[hms_idx]) + 1 + new_idx = idx + + return (new_idx, hms) + + # ------------------------------------------------------------------ + # Handling for individual tokens. These are kept as methods instead + # of functions for the sake of customizability via subclassing. + + def _parsems(self, value): + """Parse a I[.F] seconds value into (seconds, microseconds).""" + if "." not in value: + return int(value), 0 + else: + i, f = value.split(".") + return int(i), int(f.ljust(6, "0")[:6]) + + def _to_decimal(self, val): + try: + decimal_value = Decimal(val) + # See GH 662, edge case, infinite value should not be converted + # via `_to_decimal` + if not decimal_value.is_finite(): + raise ValueError("Converted decimal value is infinite or NaN") + except Exception as e: + msg = "Could not convert %s to decimal" % val + six.raise_from(ValueError(msg), e) + else: + return decimal_value + + # ------------------------------------------------------------------ + # Post-Parsing construction of datetime output. These are kept as + # methods instead of functions for the sake of customizability via + # subclassing. + + def _build_tzinfo(self, tzinfos, tzname, tzoffset): + if callable(tzinfos): + tzdata = tzinfos(tzname, tzoffset) + else: + tzdata = tzinfos.get(tzname) + # handle case where tzinfo is paased an options that returns None + # eg tzinfos = {'BRST' : None} + if isinstance(tzdata, datetime.tzinfo) or tzdata is None: + tzinfo = tzdata + elif isinstance(tzdata, text_type): + tzinfo = tz.tzstr(tzdata) + elif isinstance(tzdata, integer_types): + tzinfo = tz.tzoffset(tzname, tzdata) + else: + raise TypeError("Offset must be tzinfo subclass, tz string, " + "or int offset.") + return tzinfo + + def _build_tzaware(self, naive, res, tzinfos): + if (callable(tzinfos) or (tzinfos and res.tzname in tzinfos)): + tzinfo = self._build_tzinfo(tzinfos, res.tzname, res.tzoffset) + aware = naive.replace(tzinfo=tzinfo) + aware = self._assign_tzname(aware, res.tzname) + + elif res.tzname and res.tzname in time.tzname: + aware = naive.replace(tzinfo=tz.tzlocal()) + + # Handle ambiguous local datetime + aware = self._assign_tzname(aware, res.tzname) + + # This is mostly relevant for winter GMT zones parsed in the UK + if (aware.tzname() != res.tzname and + res.tzname in self.info.UTCZONE): + aware = aware.replace(tzinfo=tz.UTC) + + elif res.tzoffset == 0: + aware = naive.replace(tzinfo=tz.UTC) + + elif res.tzoffset: + aware = naive.replace(tzinfo=tz.tzoffset(res.tzname, res.tzoffset)) + + elif not res.tzname and not res.tzoffset: + # i.e. no timezone information was found. + aware = naive + + elif res.tzname: + # tz-like string was parsed but we don't know what to do + # with it + warnings.warn("tzname {tzname} identified but not understood. " + "Pass `tzinfos` argument in order to correctly " + "return a timezone-aware datetime. In a future " + "version, this will raise an " + "exception.".format(tzname=res.tzname), + category=UnknownTimezoneWarning) + aware = naive + + return aware + + def _build_naive(self, res, default): + repl = {} + for attr in ("year", "month", "day", "hour", + "minute", "second", "microsecond"): + value = getattr(res, attr) + if value is not None: + repl[attr] = value + + if 'day' not in repl: + # If the default day exceeds the last day of the month, fall back + # to the end of the month. + cyear = default.year if res.year is None else res.year + cmonth = default.month if res.month is None else res.month + cday = default.day if res.day is None else res.day + + if cday > monthrange(cyear, cmonth)[1]: + repl['day'] = monthrange(cyear, cmonth)[1] + + naive = default.replace(**repl) + + if res.weekday is not None and not res.day: + naive = naive + relativedelta.relativedelta(weekday=res.weekday) + + return naive + + def _assign_tzname(self, dt, tzname): + if dt.tzname() != tzname: + new_dt = tz.enfold(dt, fold=1) + if new_dt.tzname() == tzname: + return new_dt + + return dt + + def _recombine_skipped(self, tokens, skipped_idxs): + """ + >>> tokens = ["foo", " ", "bar", " ", "19June2000", "baz"] + >>> skipped_idxs = [0, 1, 2, 5] + >>> _recombine_skipped(tokens, skipped_idxs) + ["foo bar", "baz"] + """ + skipped_tokens = [] + for i, idx in enumerate(sorted(skipped_idxs)): + if i > 0 and idx - 1 == skipped_idxs[i - 1]: + skipped_tokens[-1] = skipped_tokens[-1] + tokens[idx] + else: + skipped_tokens.append(tokens[idx]) + + return skipped_tokens + + +DEFAULTPARSER = parser() + + +def parse(timestr, parserinfo=None, **kwargs): + """ + + Parse a string in one of the supported formats, using the + ``parserinfo`` parameters. + + :param timestr: + A string containing a date/time stamp. + + :param parserinfo: + A :class:`parserinfo` object containing parameters for the parser. + If ``None``, the default arguments to the :class:`parserinfo` + constructor are used. + + The ``**kwargs`` parameter takes the following keyword arguments: + + :param default: + The default datetime object, if this is a datetime object and not + ``None``, elements specified in ``timestr`` replace elements in the + default object. + + :param ignoretz: + If set ``True``, time zones in parsed strings are ignored and a naive + :class:`datetime` object is returned. + + :param tzinfos: + Additional time zone names / aliases which may be present in the + string. This argument maps time zone names (and optionally offsets + from those time zones) to time zones. This parameter can be a + dictionary with timezone aliases mapping time zone names to time + zones or a function taking two parameters (``tzname`` and + ``tzoffset``) and returning a time zone. + + The timezones to which the names are mapped can be an integer + offset from UTC in seconds or a :class:`tzinfo` object. + + .. doctest:: + :options: +NORMALIZE_WHITESPACE + + >>> from dateutil.parser import parse + >>> from dateutil.tz import gettz + >>> tzinfos = {"BRST": -7200, "CST": gettz("America/Chicago")} + >>> parse("2012-01-19 17:21:00 BRST", tzinfos=tzinfos) + datetime.datetime(2012, 1, 19, 17, 21, tzinfo=tzoffset(u'BRST', -7200)) + >>> parse("2012-01-19 17:21:00 CST", tzinfos=tzinfos) + datetime.datetime(2012, 1, 19, 17, 21, + tzinfo=tzfile('/usr/share/zoneinfo/America/Chicago')) + + This parameter is ignored if ``ignoretz`` is set. + + :param dayfirst: + Whether to interpret the first value in an ambiguous 3-integer date + (e.g. 01/05/09) as the day (``True``) or month (``False``). If + ``yearfirst`` is set to ``True``, this distinguishes between YDM and + YMD. If set to ``None``, this value is retrieved from the current + :class:`parserinfo` object (which itself defaults to ``False``). + + :param yearfirst: + Whether to interpret the first value in an ambiguous 3-integer date + (e.g. 01/05/09) as the year. If ``True``, the first number is taken to + be the year, otherwise the last number is taken to be the year. If + this is set to ``None``, the value is retrieved from the current + :class:`parserinfo` object (which itself defaults to ``False``). + + :param fuzzy: + Whether to allow fuzzy parsing, allowing for string like "Today is + January 1, 2047 at 8:21:00AM". + + :param fuzzy_with_tokens: + If ``True``, ``fuzzy`` is automatically set to True, and the parser + will return a tuple where the first element is the parsed + :class:`datetime.datetime` datetimestamp and the second element is + a tuple containing the portions of the string which were ignored: + + .. doctest:: + + >>> from dateutil.parser import parse + >>> parse("Today is January 1, 2047 at 8:21:00AM", fuzzy_with_tokens=True) + (datetime.datetime(2047, 1, 1, 8, 21), (u'Today is ', u' ', u'at ')) + + :return: + Returns a :class:`datetime.datetime` object or, if the + ``fuzzy_with_tokens`` option is ``True``, returns a tuple, the + first element being a :class:`datetime.datetime` object, the second + a tuple containing the fuzzy tokens. + + :raises ParserError: + Raised for invalid or unknown string formats, if the provided + :class:`tzinfo` is not in a valid format, or if an invalid date would + be created. + + :raises OverflowError: + Raised if the parsed date exceeds the largest valid C integer on + your system. + """ + if parserinfo: + return parser(parserinfo).parse(timestr, **kwargs) + else: + return DEFAULTPARSER.parse(timestr, **kwargs) + + +class _tzparser(object): + + class _result(_resultbase): + + __slots__ = ["stdabbr", "stdoffset", "dstabbr", "dstoffset", + "start", "end"] + + class _attr(_resultbase): + __slots__ = ["month", "week", "weekday", + "yday", "jyday", "day", "time"] + + def __repr__(self): + return self._repr("") + + def __init__(self): + _resultbase.__init__(self) + self.start = self._attr() + self.end = self._attr() + + def parse(self, tzstr): + res = self._result() + l = [x for x in re.split(r'([,:.]|[a-zA-Z]+|[0-9]+)',tzstr) if x] + used_idxs = list() + try: + + len_l = len(l) + + i = 0 + while i < len_l: + # BRST+3[BRDT[+2]] + j = i + while j < len_l and not [x for x in l[j] + if x in "0123456789:,-+"]: + j += 1 + if j != i: + if not res.stdabbr: + offattr = "stdoffset" + res.stdabbr = "".join(l[i:j]) + else: + offattr = "dstoffset" + res.dstabbr = "".join(l[i:j]) + + for ii in range(j): + used_idxs.append(ii) + i = j + if (i < len_l and (l[i] in ('+', '-') or l[i][0] in + "0123456789")): + if l[i] in ('+', '-'): + # Yes, that's right. See the TZ variable + # documentation. + signal = (1, -1)[l[i] == '+'] + used_idxs.append(i) + i += 1 + else: + signal = -1 + len_li = len(l[i]) + if len_li == 4: + # -0300 + setattr(res, offattr, (int(l[i][:2]) * 3600 + + int(l[i][2:]) * 60) * signal) + elif i + 1 < len_l and l[i + 1] == ':': + # -03:00 + setattr(res, offattr, + (int(l[i]) * 3600 + + int(l[i + 2]) * 60) * signal) + used_idxs.append(i) + i += 2 + elif len_li <= 2: + # -[0]3 + setattr(res, offattr, + int(l[i][:2]) * 3600 * signal) + else: + return None + used_idxs.append(i) + i += 1 + if res.dstabbr: + break + else: + break + + + if i < len_l: + for j in range(i, len_l): + if l[j] == ';': + l[j] = ',' + + assert l[i] == ',' + + i += 1 + + if i >= len_l: + pass + elif (8 <= l.count(',') <= 9 and + not [y for x in l[i:] if x != ',' + for y in x if y not in "0123456789+-"]): + # GMT0BST,3,0,30,3600,10,0,26,7200[,3600] + for x in (res.start, res.end): + x.month = int(l[i]) + used_idxs.append(i) + i += 2 + if l[i] == '-': + value = int(l[i + 1]) * -1 + used_idxs.append(i) + i += 1 + else: + value = int(l[i]) + used_idxs.append(i) + i += 2 + if value: + x.week = value + x.weekday = (int(l[i]) - 1) % 7 + else: + x.day = int(l[i]) + used_idxs.append(i) + i += 2 + x.time = int(l[i]) + used_idxs.append(i) + i += 2 + if i < len_l: + if l[i] in ('-', '+'): + signal = (-1, 1)[l[i] == "+"] + used_idxs.append(i) + i += 1 + else: + signal = 1 + used_idxs.append(i) + res.dstoffset = (res.stdoffset + int(l[i]) * signal) + + # This was a made-up format that is not in normal use + warn(('Parsed time zone "%s"' % tzstr) + + 'is in a non-standard dateutil-specific format, which ' + + 'is now deprecated; support for parsing this format ' + + 'will be removed in future versions. It is recommended ' + + 'that you switch to a standard format like the GNU ' + + 'TZ variable format.', tz.DeprecatedTzFormatWarning) + elif (l.count(',') == 2 and l[i:].count('/') <= 2 and + not [y for x in l[i:] if x not in (',', '/', 'J', 'M', + '.', '-', ':') + for y in x if y not in "0123456789"]): + for x in (res.start, res.end): + if l[i] == 'J': + # non-leap year day (1 based) + used_idxs.append(i) + i += 1 + x.jyday = int(l[i]) + elif l[i] == 'M': + # month[-.]week[-.]weekday + used_idxs.append(i) + i += 1 + x.month = int(l[i]) + used_idxs.append(i) + i += 1 + assert l[i] in ('-', '.') + used_idxs.append(i) + i += 1 + x.week = int(l[i]) + if x.week == 5: + x.week = -1 + used_idxs.append(i) + i += 1 + assert l[i] in ('-', '.') + used_idxs.append(i) + i += 1 + x.weekday = (int(l[i]) - 1) % 7 + else: + # year day (zero based) + x.yday = int(l[i]) + 1 + + used_idxs.append(i) + i += 1 + + if i < len_l and l[i] == '/': + used_idxs.append(i) + i += 1 + # start time + len_li = len(l[i]) + if len_li == 4: + # -0300 + x.time = (int(l[i][:2]) * 3600 + + int(l[i][2:]) * 60) + elif i + 1 < len_l and l[i + 1] == ':': + # -03:00 + x.time = int(l[i]) * 3600 + int(l[i + 2]) * 60 + used_idxs.append(i) + i += 2 + if i + 1 < len_l and l[i + 1] == ':': + used_idxs.append(i) + i += 2 + x.time += int(l[i]) + elif len_li <= 2: + # -[0]3 + x.time = (int(l[i][:2]) * 3600) + else: + return None + used_idxs.append(i) + i += 1 + + assert i == len_l or l[i] == ',' + + i += 1 + + assert i >= len_l + + except (IndexError, ValueError, AssertionError): + return None + + unused_idxs = set(range(len_l)).difference(used_idxs) + res.any_unused_tokens = not {l[n] for n in unused_idxs}.issubset({",",":"}) + return res + + +DEFAULTTZPARSER = _tzparser() + + +def _parsetz(tzstr): + return DEFAULTTZPARSER.parse(tzstr) + + +class ParserError(ValueError): + """Exception subclass used for any failure to parse a datetime string. + + This is a subclass of :py:exc:`ValueError`, and should be raised any time + earlier versions of ``dateutil`` would have raised ``ValueError``. + + .. versionadded:: 2.8.1 + """ + def __str__(self): + try: + return self.args[0] % self.args[1:] + except (TypeError, IndexError): + return super(ParserError, self).__str__() + + def __repr__(self): + args = ", ".join("'%s'" % arg for arg in self.args) + return "%s(%s)" % (self.__class__.__name__, args) + + +class UnknownTimezoneWarning(RuntimeWarning): + """Raised when the parser finds a timezone it cannot parse into a tzinfo. + + .. versionadded:: 2.7.0 + """ +# vim:ts=4:sw=4:et diff --git a/venv/lib/python3.12/site-packages/dateutil/parser/isoparser.py b/venv/lib/python3.12/site-packages/dateutil/parser/isoparser.py new file mode 100644 index 00000000..7060087d --- /dev/null +++ b/venv/lib/python3.12/site-packages/dateutil/parser/isoparser.py @@ -0,0 +1,416 @@ +# -*- coding: utf-8 -*- +""" +This module offers a parser for ISO-8601 strings + +It is intended to support all valid date, time and datetime formats per the +ISO-8601 specification. + +..versionadded:: 2.7.0 +""" +from datetime import datetime, timedelta, time, date +import calendar +from dateutil import tz + +from functools import wraps + +import re +import six + +__all__ = ["isoparse", "isoparser"] + + +def _takes_ascii(f): + @wraps(f) + def func(self, str_in, *args, **kwargs): + # If it's a stream, read the whole thing + str_in = getattr(str_in, 'read', lambda: str_in)() + + # If it's unicode, turn it into bytes, since ISO-8601 only covers ASCII + if isinstance(str_in, six.text_type): + # ASCII is the same in UTF-8 + try: + str_in = str_in.encode('ascii') + except UnicodeEncodeError as e: + msg = 'ISO-8601 strings should contain only ASCII characters' + six.raise_from(ValueError(msg), e) + + return f(self, str_in, *args, **kwargs) + + return func + + +class isoparser(object): + def __init__(self, sep=None): + """ + :param sep: + A single character that separates date and time portions. If + ``None``, the parser will accept any single character. + For strict ISO-8601 adherence, pass ``'T'``. + """ + if sep is not None: + if (len(sep) != 1 or ord(sep) >= 128 or sep in '0123456789'): + raise ValueError('Separator must be a single, non-numeric ' + + 'ASCII character') + + sep = sep.encode('ascii') + + self._sep = sep + + @_takes_ascii + def isoparse(self, dt_str): + """ + Parse an ISO-8601 datetime string into a :class:`datetime.datetime`. + + An ISO-8601 datetime string consists of a date portion, followed + optionally by a time portion - the date and time portions are separated + by a single character separator, which is ``T`` in the official + standard. Incomplete date formats (such as ``YYYY-MM``) may *not* be + combined with a time portion. + + Supported date formats are: + + Common: + + - ``YYYY`` + - ``YYYY-MM`` + - ``YYYY-MM-DD`` or ``YYYYMMDD`` + + Uncommon: + + - ``YYYY-Www`` or ``YYYYWww`` - ISO week (day defaults to 0) + - ``YYYY-Www-D`` or ``YYYYWwwD`` - ISO week and day + + The ISO week and day numbering follows the same logic as + :func:`datetime.date.isocalendar`. + + Supported time formats are: + + - ``hh`` + - ``hh:mm`` or ``hhmm`` + - ``hh:mm:ss`` or ``hhmmss`` + - ``hh:mm:ss.ssssss`` (Up to 6 sub-second digits) + + Midnight is a special case for `hh`, as the standard supports both + 00:00 and 24:00 as a representation. The decimal separator can be + either a dot or a comma. + + + .. caution:: + + Support for fractional components other than seconds is part of the + ISO-8601 standard, but is not currently implemented in this parser. + + Supported time zone offset formats are: + + - `Z` (UTC) + - `±HH:MM` + - `±HHMM` + - `±HH` + + Offsets will be represented as :class:`dateutil.tz.tzoffset` objects, + with the exception of UTC, which will be represented as + :class:`dateutil.tz.tzutc`. Time zone offsets equivalent to UTC (such + as `+00:00`) will also be represented as :class:`dateutil.tz.tzutc`. + + :param dt_str: + A string or stream containing only an ISO-8601 datetime string + + :return: + Returns a :class:`datetime.datetime` representing the string. + Unspecified components default to their lowest value. + + .. warning:: + + As of version 2.7.0, the strictness of the parser should not be + considered a stable part of the contract. Any valid ISO-8601 string + that parses correctly with the default settings will continue to + parse correctly in future versions, but invalid strings that + currently fail (e.g. ``2017-01-01T00:00+00:00:00``) are not + guaranteed to continue failing in future versions if they encode + a valid date. + + .. versionadded:: 2.7.0 + """ + components, pos = self._parse_isodate(dt_str) + + if len(dt_str) > pos: + if self._sep is None or dt_str[pos:pos + 1] == self._sep: + components += self._parse_isotime(dt_str[pos + 1:]) + else: + raise ValueError('String contains unknown ISO components') + + if len(components) > 3 and components[3] == 24: + components[3] = 0 + return datetime(*components) + timedelta(days=1) + + return datetime(*components) + + @_takes_ascii + def parse_isodate(self, datestr): + """ + Parse the date portion of an ISO string. + + :param datestr: + The string portion of an ISO string, without a separator + + :return: + Returns a :class:`datetime.date` object + """ + components, pos = self._parse_isodate(datestr) + if pos < len(datestr): + raise ValueError('String contains unknown ISO ' + + 'components: {!r}'.format(datestr.decode('ascii'))) + return date(*components) + + @_takes_ascii + def parse_isotime(self, timestr): + """ + Parse the time portion of an ISO string. + + :param timestr: + The time portion of an ISO string, without a separator + + :return: + Returns a :class:`datetime.time` object + """ + components = self._parse_isotime(timestr) + if components[0] == 24: + components[0] = 0 + return time(*components) + + @_takes_ascii + def parse_tzstr(self, tzstr, zero_as_utc=True): + """ + Parse a valid ISO time zone string. + + See :func:`isoparser.isoparse` for details on supported formats. + + :param tzstr: + A string representing an ISO time zone offset + + :param zero_as_utc: + Whether to return :class:`dateutil.tz.tzutc` for zero-offset zones + + :return: + Returns :class:`dateutil.tz.tzoffset` for offsets and + :class:`dateutil.tz.tzutc` for ``Z`` and (if ``zero_as_utc`` is + specified) offsets equivalent to UTC. + """ + return self._parse_tzstr(tzstr, zero_as_utc=zero_as_utc) + + # Constants + _DATE_SEP = b'-' + _TIME_SEP = b':' + _FRACTION_REGEX = re.compile(b'[\\.,]([0-9]+)') + + def _parse_isodate(self, dt_str): + try: + return self._parse_isodate_common(dt_str) + except ValueError: + return self._parse_isodate_uncommon(dt_str) + + def _parse_isodate_common(self, dt_str): + len_str = len(dt_str) + components = [1, 1, 1] + + if len_str < 4: + raise ValueError('ISO string too short') + + # Year + components[0] = int(dt_str[0:4]) + pos = 4 + if pos >= len_str: + return components, pos + + has_sep = dt_str[pos:pos + 1] == self._DATE_SEP + if has_sep: + pos += 1 + + # Month + if len_str - pos < 2: + raise ValueError('Invalid common month') + + components[1] = int(dt_str[pos:pos + 2]) + pos += 2 + + if pos >= len_str: + if has_sep: + return components, pos + else: + raise ValueError('Invalid ISO format') + + if has_sep: + if dt_str[pos:pos + 1] != self._DATE_SEP: + raise ValueError('Invalid separator in ISO string') + pos += 1 + + # Day + if len_str - pos < 2: + raise ValueError('Invalid common day') + components[2] = int(dt_str[pos:pos + 2]) + return components, pos + 2 + + def _parse_isodate_uncommon(self, dt_str): + if len(dt_str) < 4: + raise ValueError('ISO string too short') + + # All ISO formats start with the year + year = int(dt_str[0:4]) + + has_sep = dt_str[4:5] == self._DATE_SEP + + pos = 4 + has_sep # Skip '-' if it's there + if dt_str[pos:pos + 1] == b'W': + # YYYY-?Www-?D? + pos += 1 + weekno = int(dt_str[pos:pos + 2]) + pos += 2 + + dayno = 1 + if len(dt_str) > pos: + if (dt_str[pos:pos + 1] == self._DATE_SEP) != has_sep: + raise ValueError('Inconsistent use of dash separator') + + pos += has_sep + + dayno = int(dt_str[pos:pos + 1]) + pos += 1 + + base_date = self._calculate_weekdate(year, weekno, dayno) + else: + # YYYYDDD or YYYY-DDD + if len(dt_str) - pos < 3: + raise ValueError('Invalid ordinal day') + + ordinal_day = int(dt_str[pos:pos + 3]) + pos += 3 + + if ordinal_day < 1 or ordinal_day > (365 + calendar.isleap(year)): + raise ValueError('Invalid ordinal day' + + ' {} for year {}'.format(ordinal_day, year)) + + base_date = date(year, 1, 1) + timedelta(days=ordinal_day - 1) + + components = [base_date.year, base_date.month, base_date.day] + return components, pos + + def _calculate_weekdate(self, year, week, day): + """ + Calculate the day of corresponding to the ISO year-week-day calendar. + + This function is effectively the inverse of + :func:`datetime.date.isocalendar`. + + :param year: + The year in the ISO calendar + + :param week: + The week in the ISO calendar - range is [1, 53] + + :param day: + The day in the ISO calendar - range is [1 (MON), 7 (SUN)] + + :return: + Returns a :class:`datetime.date` + """ + if not 0 < week < 54: + raise ValueError('Invalid week: {}'.format(week)) + + if not 0 < day < 8: # Range is 1-7 + raise ValueError('Invalid weekday: {}'.format(day)) + + # Get week 1 for the specific year: + jan_4 = date(year, 1, 4) # Week 1 always has January 4th in it + week_1 = jan_4 - timedelta(days=jan_4.isocalendar()[2] - 1) + + # Now add the specific number of weeks and days to get what we want + week_offset = (week - 1) * 7 + (day - 1) + return week_1 + timedelta(days=week_offset) + + def _parse_isotime(self, timestr): + len_str = len(timestr) + components = [0, 0, 0, 0, None] + pos = 0 + comp = -1 + + if len_str < 2: + raise ValueError('ISO time too short') + + has_sep = False + + while pos < len_str and comp < 5: + comp += 1 + + if timestr[pos:pos + 1] in b'-+Zz': + # Detect time zone boundary + components[-1] = self._parse_tzstr(timestr[pos:]) + pos = len_str + break + + if comp == 1 and timestr[pos:pos+1] == self._TIME_SEP: + has_sep = True + pos += 1 + elif comp == 2 and has_sep: + if timestr[pos:pos+1] != self._TIME_SEP: + raise ValueError('Inconsistent use of colon separator') + pos += 1 + + if comp < 3: + # Hour, minute, second + components[comp] = int(timestr[pos:pos + 2]) + pos += 2 + + if comp == 3: + # Fraction of a second + frac = self._FRACTION_REGEX.match(timestr[pos:]) + if not frac: + continue + + us_str = frac.group(1)[:6] # Truncate to microseconds + components[comp] = int(us_str) * 10**(6 - len(us_str)) + pos += len(frac.group()) + + if pos < len_str: + raise ValueError('Unused components in ISO string') + + if components[0] == 24: + # Standard supports 00:00 and 24:00 as representations of midnight + if any(component != 0 for component in components[1:4]): + raise ValueError('Hour may only be 24 at 24:00:00.000') + + return components + + def _parse_tzstr(self, tzstr, zero_as_utc=True): + if tzstr == b'Z' or tzstr == b'z': + return tz.UTC + + if len(tzstr) not in {3, 5, 6}: + raise ValueError('Time zone offset must be 1, 3, 5 or 6 characters') + + if tzstr[0:1] == b'-': + mult = -1 + elif tzstr[0:1] == b'+': + mult = 1 + else: + raise ValueError('Time zone offset requires sign') + + hours = int(tzstr[1:3]) + if len(tzstr) == 3: + minutes = 0 + else: + minutes = int(tzstr[(4 if tzstr[3:4] == self._TIME_SEP else 3):]) + + if zero_as_utc and hours == 0 and minutes == 0: + return tz.UTC + else: + if minutes > 59: + raise ValueError('Invalid minutes in time zone offset') + + if hours > 23: + raise ValueError('Invalid hours in time zone offset') + + return tz.tzoffset(None, mult * (hours * 60 + minutes) * 60) + + +DEFAULT_ISOPARSER = isoparser() +isoparse = DEFAULT_ISOPARSER.isoparse diff --git a/venv/lib/python3.12/site-packages/dateutil/relativedelta.py b/venv/lib/python3.12/site-packages/dateutil/relativedelta.py new file mode 100644 index 00000000..cd323a54 --- /dev/null +++ b/venv/lib/python3.12/site-packages/dateutil/relativedelta.py @@ -0,0 +1,599 @@ +# -*- coding: utf-8 -*- +import datetime +import calendar + +import operator +from math import copysign + +from six import integer_types +from warnings import warn + +from ._common import weekday + +MO, TU, WE, TH, FR, SA, SU = weekdays = tuple(weekday(x) for x in range(7)) + +__all__ = ["relativedelta", "MO", "TU", "WE", "TH", "FR", "SA", "SU"] + + +class relativedelta(object): + """ + The relativedelta type is designed to be applied to an existing datetime and + can replace specific components of that datetime, or represents an interval + of time. + + It is based on the specification of the excellent work done by M.-A. Lemburg + in his + `mx.DateTime `_ extension. + However, notice that this type does *NOT* implement the same algorithm as + his work. Do *NOT* expect it to behave like mx.DateTime's counterpart. + + There are two different ways to build a relativedelta instance. The + first one is passing it two date/datetime classes:: + + relativedelta(datetime1, datetime2) + + The second one is passing it any number of the following keyword arguments:: + + relativedelta(arg1=x,arg2=y,arg3=z...) + + year, month, day, hour, minute, second, microsecond: + Absolute information (argument is singular); adding or subtracting a + relativedelta with absolute information does not perform an arithmetic + operation, but rather REPLACES the corresponding value in the + original datetime with the value(s) in relativedelta. + + years, months, weeks, days, hours, minutes, seconds, microseconds: + Relative information, may be negative (argument is plural); adding + or subtracting a relativedelta with relative information performs + the corresponding arithmetic operation on the original datetime value + with the information in the relativedelta. + + weekday: + One of the weekday instances (MO, TU, etc) available in the + relativedelta module. These instances may receive a parameter N, + specifying the Nth weekday, which could be positive or negative + (like MO(+1) or MO(-2)). Not specifying it is the same as specifying + +1. You can also use an integer, where 0=MO. This argument is always + relative e.g. if the calculated date is already Monday, using MO(1) + or MO(-1) won't change the day. To effectively make it absolute, use + it in combination with the day argument (e.g. day=1, MO(1) for first + Monday of the month). + + leapdays: + Will add given days to the date found, if year is a leap + year, and the date found is post 28 of february. + + yearday, nlyearday: + Set the yearday or the non-leap year day (jump leap days). + These are converted to day/month/leapdays information. + + There are relative and absolute forms of the keyword + arguments. The plural is relative, and the singular is + absolute. For each argument in the order below, the absolute form + is applied first (by setting each attribute to that value) and + then the relative form (by adding the value to the attribute). + + The order of attributes considered when this relativedelta is + added to a datetime is: + + 1. Year + 2. Month + 3. Day + 4. Hours + 5. Minutes + 6. Seconds + 7. Microseconds + + Finally, weekday is applied, using the rule described above. + + For example + + >>> from datetime import datetime + >>> from dateutil.relativedelta import relativedelta, MO + >>> dt = datetime(2018, 4, 9, 13, 37, 0) + >>> delta = relativedelta(hours=25, day=1, weekday=MO(1)) + >>> dt + delta + datetime.datetime(2018, 4, 2, 14, 37) + + First, the day is set to 1 (the first of the month), then 25 hours + are added, to get to the 2nd day and 14th hour, finally the + weekday is applied, but since the 2nd is already a Monday there is + no effect. + + """ + + def __init__(self, dt1=None, dt2=None, + years=0, months=0, days=0, leapdays=0, weeks=0, + hours=0, minutes=0, seconds=0, microseconds=0, + year=None, month=None, day=None, weekday=None, + yearday=None, nlyearday=None, + hour=None, minute=None, second=None, microsecond=None): + + if dt1 and dt2: + # datetime is a subclass of date. So both must be date + if not (isinstance(dt1, datetime.date) and + isinstance(dt2, datetime.date)): + raise TypeError("relativedelta only diffs datetime/date") + + # We allow two dates, or two datetimes, so we coerce them to be + # of the same type + if (isinstance(dt1, datetime.datetime) != + isinstance(dt2, datetime.datetime)): + if not isinstance(dt1, datetime.datetime): + dt1 = datetime.datetime.fromordinal(dt1.toordinal()) + elif not isinstance(dt2, datetime.datetime): + dt2 = datetime.datetime.fromordinal(dt2.toordinal()) + + self.years = 0 + self.months = 0 + self.days = 0 + self.leapdays = 0 + self.hours = 0 + self.minutes = 0 + self.seconds = 0 + self.microseconds = 0 + self.year = None + self.month = None + self.day = None + self.weekday = None + self.hour = None + self.minute = None + self.second = None + self.microsecond = None + self._has_time = 0 + + # Get year / month delta between the two + months = (dt1.year - dt2.year) * 12 + (dt1.month - dt2.month) + self._set_months(months) + + # Remove the year/month delta so the timedelta is just well-defined + # time units (seconds, days and microseconds) + dtm = self.__radd__(dt2) + + # If we've overshot our target, make an adjustment + if dt1 < dt2: + compare = operator.gt + increment = 1 + else: + compare = operator.lt + increment = -1 + + while compare(dt1, dtm): + months += increment + self._set_months(months) + dtm = self.__radd__(dt2) + + # Get the timedelta between the "months-adjusted" date and dt1 + delta = dt1 - dtm + self.seconds = delta.seconds + delta.days * 86400 + self.microseconds = delta.microseconds + else: + # Check for non-integer values in integer-only quantities + if any(x is not None and x != int(x) for x in (years, months)): + raise ValueError("Non-integer years and months are " + "ambiguous and not currently supported.") + + # Relative information + self.years = int(years) + self.months = int(months) + self.days = days + weeks * 7 + self.leapdays = leapdays + self.hours = hours + self.minutes = minutes + self.seconds = seconds + self.microseconds = microseconds + + # Absolute information + self.year = year + self.month = month + self.day = day + self.hour = hour + self.minute = minute + self.second = second + self.microsecond = microsecond + + if any(x is not None and int(x) != x + for x in (year, month, day, hour, + minute, second, microsecond)): + # For now we'll deprecate floats - later it'll be an error. + warn("Non-integer value passed as absolute information. " + + "This is not a well-defined condition and will raise " + + "errors in future versions.", DeprecationWarning) + + if isinstance(weekday, integer_types): + self.weekday = weekdays[weekday] + else: + self.weekday = weekday + + yday = 0 + if nlyearday: + yday = nlyearday + elif yearday: + yday = yearday + if yearday > 59: + self.leapdays = -1 + if yday: + ydayidx = [31, 59, 90, 120, 151, 181, 212, + 243, 273, 304, 334, 366] + for idx, ydays in enumerate(ydayidx): + if yday <= ydays: + self.month = idx+1 + if idx == 0: + self.day = yday + else: + self.day = yday-ydayidx[idx-1] + break + else: + raise ValueError("invalid year day (%d)" % yday) + + self._fix() + + def _fix(self): + if abs(self.microseconds) > 999999: + s = _sign(self.microseconds) + div, mod = divmod(self.microseconds * s, 1000000) + self.microseconds = mod * s + self.seconds += div * s + if abs(self.seconds) > 59: + s = _sign(self.seconds) + div, mod = divmod(self.seconds * s, 60) + self.seconds = mod * s + self.minutes += div * s + if abs(self.minutes) > 59: + s = _sign(self.minutes) + div, mod = divmod(self.minutes * s, 60) + self.minutes = mod * s + self.hours += div * s + if abs(self.hours) > 23: + s = _sign(self.hours) + div, mod = divmod(self.hours * s, 24) + self.hours = mod * s + self.days += div * s + if abs(self.months) > 11: + s = _sign(self.months) + div, mod = divmod(self.months * s, 12) + self.months = mod * s + self.years += div * s + if (self.hours or self.minutes or self.seconds or self.microseconds + or self.hour is not None or self.minute is not None or + self.second is not None or self.microsecond is not None): + self._has_time = 1 + else: + self._has_time = 0 + + @property + def weeks(self): + return int(self.days / 7.0) + + @weeks.setter + def weeks(self, value): + self.days = self.days - (self.weeks * 7) + value * 7 + + def _set_months(self, months): + self.months = months + if abs(self.months) > 11: + s = _sign(self.months) + div, mod = divmod(self.months * s, 12) + self.months = mod * s + self.years = div * s + else: + self.years = 0 + + def normalized(self): + """ + Return a version of this object represented entirely using integer + values for the relative attributes. + + >>> relativedelta(days=1.5, hours=2).normalized() + relativedelta(days=+1, hours=+14) + + :return: + Returns a :class:`dateutil.relativedelta.relativedelta` object. + """ + # Cascade remainders down (rounding each to roughly nearest microsecond) + days = int(self.days) + + hours_f = round(self.hours + 24 * (self.days - days), 11) + hours = int(hours_f) + + minutes_f = round(self.minutes + 60 * (hours_f - hours), 10) + minutes = int(minutes_f) + + seconds_f = round(self.seconds + 60 * (minutes_f - minutes), 8) + seconds = int(seconds_f) + + microseconds = round(self.microseconds + 1e6 * (seconds_f - seconds)) + + # Constructor carries overflow back up with call to _fix() + return self.__class__(years=self.years, months=self.months, + days=days, hours=hours, minutes=minutes, + seconds=seconds, microseconds=microseconds, + leapdays=self.leapdays, year=self.year, + month=self.month, day=self.day, + weekday=self.weekday, hour=self.hour, + minute=self.minute, second=self.second, + microsecond=self.microsecond) + + def __add__(self, other): + if isinstance(other, relativedelta): + return self.__class__(years=other.years + self.years, + months=other.months + self.months, + days=other.days + self.days, + hours=other.hours + self.hours, + minutes=other.minutes + self.minutes, + seconds=other.seconds + self.seconds, + microseconds=(other.microseconds + + self.microseconds), + leapdays=other.leapdays or self.leapdays, + year=(other.year if other.year is not None + else self.year), + month=(other.month if other.month is not None + else self.month), + day=(other.day if other.day is not None + else self.day), + weekday=(other.weekday if other.weekday is not None + else self.weekday), + hour=(other.hour if other.hour is not None + else self.hour), + minute=(other.minute if other.minute is not None + else self.minute), + second=(other.second if other.second is not None + else self.second), + microsecond=(other.microsecond if other.microsecond + is not None else + self.microsecond)) + if isinstance(other, datetime.timedelta): + return self.__class__(years=self.years, + months=self.months, + days=self.days + other.days, + hours=self.hours, + minutes=self.minutes, + seconds=self.seconds + other.seconds, + microseconds=self.microseconds + other.microseconds, + leapdays=self.leapdays, + year=self.year, + month=self.month, + day=self.day, + weekday=self.weekday, + hour=self.hour, + minute=self.minute, + second=self.second, + microsecond=self.microsecond) + if not isinstance(other, datetime.date): + return NotImplemented + elif self._has_time and not isinstance(other, datetime.datetime): + other = datetime.datetime.fromordinal(other.toordinal()) + year = (self.year or other.year)+self.years + month = self.month or other.month + if self.months: + assert 1 <= abs(self.months) <= 12 + month += self.months + if month > 12: + year += 1 + month -= 12 + elif month < 1: + year -= 1 + month += 12 + day = min(calendar.monthrange(year, month)[1], + self.day or other.day) + repl = {"year": year, "month": month, "day": day} + for attr in ["hour", "minute", "second", "microsecond"]: + value = getattr(self, attr) + if value is not None: + repl[attr] = value + days = self.days + if self.leapdays and month > 2 and calendar.isleap(year): + days += self.leapdays + ret = (other.replace(**repl) + + datetime.timedelta(days=days, + hours=self.hours, + minutes=self.minutes, + seconds=self.seconds, + microseconds=self.microseconds)) + if self.weekday: + weekday, nth = self.weekday.weekday, self.weekday.n or 1 + jumpdays = (abs(nth) - 1) * 7 + if nth > 0: + jumpdays += (7 - ret.weekday() + weekday) % 7 + else: + jumpdays += (ret.weekday() - weekday) % 7 + jumpdays *= -1 + ret += datetime.timedelta(days=jumpdays) + return ret + + def __radd__(self, other): + return self.__add__(other) + + def __rsub__(self, other): + return self.__neg__().__radd__(other) + + def __sub__(self, other): + if not isinstance(other, relativedelta): + return NotImplemented # In case the other object defines __rsub__ + return self.__class__(years=self.years - other.years, + months=self.months - other.months, + days=self.days - other.days, + hours=self.hours - other.hours, + minutes=self.minutes - other.minutes, + seconds=self.seconds - other.seconds, + microseconds=self.microseconds - other.microseconds, + leapdays=self.leapdays or other.leapdays, + year=(self.year if self.year is not None + else other.year), + month=(self.month if self.month is not None else + other.month), + day=(self.day if self.day is not None else + other.day), + weekday=(self.weekday if self.weekday is not None else + other.weekday), + hour=(self.hour if self.hour is not None else + other.hour), + minute=(self.minute if self.minute is not None else + other.minute), + second=(self.second if self.second is not None else + other.second), + microsecond=(self.microsecond if self.microsecond + is not None else + other.microsecond)) + + def __abs__(self): + return self.__class__(years=abs(self.years), + months=abs(self.months), + days=abs(self.days), + hours=abs(self.hours), + minutes=abs(self.minutes), + seconds=abs(self.seconds), + microseconds=abs(self.microseconds), + leapdays=self.leapdays, + year=self.year, + month=self.month, + day=self.day, + weekday=self.weekday, + hour=self.hour, + minute=self.minute, + second=self.second, + microsecond=self.microsecond) + + def __neg__(self): + return self.__class__(years=-self.years, + months=-self.months, + days=-self.days, + hours=-self.hours, + minutes=-self.minutes, + seconds=-self.seconds, + microseconds=-self.microseconds, + leapdays=self.leapdays, + year=self.year, + month=self.month, + day=self.day, + weekday=self.weekday, + hour=self.hour, + minute=self.minute, + second=self.second, + microsecond=self.microsecond) + + def __bool__(self): + return not (not self.years and + not self.months and + not self.days and + not self.hours and + not self.minutes and + not self.seconds and + not self.microseconds and + not self.leapdays and + self.year is None and + self.month is None and + self.day is None and + self.weekday is None and + self.hour is None and + self.minute is None and + self.second is None and + self.microsecond is None) + # Compatibility with Python 2.x + __nonzero__ = __bool__ + + def __mul__(self, other): + try: + f = float(other) + except TypeError: + return NotImplemented + + return self.__class__(years=int(self.years * f), + months=int(self.months * f), + days=int(self.days * f), + hours=int(self.hours * f), + minutes=int(self.minutes * f), + seconds=int(self.seconds * f), + microseconds=int(self.microseconds * f), + leapdays=self.leapdays, + year=self.year, + month=self.month, + day=self.day, + weekday=self.weekday, + hour=self.hour, + minute=self.minute, + second=self.second, + microsecond=self.microsecond) + + __rmul__ = __mul__ + + def __eq__(self, other): + if not isinstance(other, relativedelta): + return NotImplemented + if self.weekday or other.weekday: + if not self.weekday or not other.weekday: + return False + if self.weekday.weekday != other.weekday.weekday: + return False + n1, n2 = self.weekday.n, other.weekday.n + if n1 != n2 and not ((not n1 or n1 == 1) and (not n2 or n2 == 1)): + return False + return (self.years == other.years and + self.months == other.months and + self.days == other.days and + self.hours == other.hours and + self.minutes == other.minutes and + self.seconds == other.seconds and + self.microseconds == other.microseconds and + self.leapdays == other.leapdays and + self.year == other.year and + self.month == other.month and + self.day == other.day and + self.hour == other.hour and + self.minute == other.minute and + self.second == other.second and + self.microsecond == other.microsecond) + + def __hash__(self): + return hash(( + self.weekday, + self.years, + self.months, + self.days, + self.hours, + self.minutes, + self.seconds, + self.microseconds, + self.leapdays, + self.year, + self.month, + self.day, + self.hour, + self.minute, + self.second, + self.microsecond, + )) + + def __ne__(self, other): + return not self.__eq__(other) + + def __div__(self, other): + try: + reciprocal = 1 / float(other) + except TypeError: + return NotImplemented + + return self.__mul__(reciprocal) + + __truediv__ = __div__ + + def __repr__(self): + l = [] + for attr in ["years", "months", "days", "leapdays", + "hours", "minutes", "seconds", "microseconds"]: + value = getattr(self, attr) + if value: + l.append("{attr}={value:+g}".format(attr=attr, value=value)) + for attr in ["year", "month", "day", "weekday", + "hour", "minute", "second", "microsecond"]: + value = getattr(self, attr) + if value is not None: + l.append("{attr}={value}".format(attr=attr, value=repr(value))) + return "{classname}({attrs})".format(classname=self.__class__.__name__, + attrs=", ".join(l)) + + +def _sign(x): + return int(copysign(1, x)) + +# vim:ts=4:sw=4:et diff --git a/venv/lib/python3.12/site-packages/dateutil/rrule.py b/venv/lib/python3.12/site-packages/dateutil/rrule.py new file mode 100644 index 00000000..571a0d2b --- /dev/null +++ b/venv/lib/python3.12/site-packages/dateutil/rrule.py @@ -0,0 +1,1737 @@ +# -*- coding: utf-8 -*- +""" +The rrule module offers a small, complete, and very fast, implementation of +the recurrence rules documented in the +`iCalendar RFC `_, +including support for caching of results. +""" +import calendar +import datetime +import heapq +import itertools +import re +import sys +from functools import wraps +# For warning about deprecation of until and count +from warnings import warn + +from six import advance_iterator, integer_types + +from six.moves import _thread, range + +from ._common import weekday as weekdaybase + +try: + from math import gcd +except ImportError: + from fractions import gcd + +__all__ = ["rrule", "rruleset", "rrulestr", + "YEARLY", "MONTHLY", "WEEKLY", "DAILY", + "HOURLY", "MINUTELY", "SECONDLY", + "MO", "TU", "WE", "TH", "FR", "SA", "SU"] + +# Every mask is 7 days longer to handle cross-year weekly periods. +M366MASK = tuple([1]*31+[2]*29+[3]*31+[4]*30+[5]*31+[6]*30 + + [7]*31+[8]*31+[9]*30+[10]*31+[11]*30+[12]*31+[1]*7) +M365MASK = list(M366MASK) +M29, M30, M31 = list(range(1, 30)), list(range(1, 31)), list(range(1, 32)) +MDAY366MASK = tuple(M31+M29+M31+M30+M31+M30+M31+M31+M30+M31+M30+M31+M31[:7]) +MDAY365MASK = list(MDAY366MASK) +M29, M30, M31 = list(range(-29, 0)), list(range(-30, 0)), list(range(-31, 0)) +NMDAY366MASK = tuple(M31+M29+M31+M30+M31+M30+M31+M31+M30+M31+M30+M31+M31[:7]) +NMDAY365MASK = list(NMDAY366MASK) +M366RANGE = (0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366) +M365RANGE = (0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365) +WDAYMASK = [0, 1, 2, 3, 4, 5, 6]*55 +del M29, M30, M31, M365MASK[59], MDAY365MASK[59], NMDAY365MASK[31] +MDAY365MASK = tuple(MDAY365MASK) +M365MASK = tuple(M365MASK) + +FREQNAMES = ['YEARLY', 'MONTHLY', 'WEEKLY', 'DAILY', 'HOURLY', 'MINUTELY', 'SECONDLY'] + +(YEARLY, + MONTHLY, + WEEKLY, + DAILY, + HOURLY, + MINUTELY, + SECONDLY) = list(range(7)) + +# Imported on demand. +easter = None +parser = None + + +class weekday(weekdaybase): + """ + This version of weekday does not allow n = 0. + """ + def __init__(self, wkday, n=None): + if n == 0: + raise ValueError("Can't create weekday with n==0") + + super(weekday, self).__init__(wkday, n) + + +MO, TU, WE, TH, FR, SA, SU = weekdays = tuple(weekday(x) for x in range(7)) + + +def _invalidates_cache(f): + """ + Decorator for rruleset methods which may invalidate the + cached length. + """ + @wraps(f) + def inner_func(self, *args, **kwargs): + rv = f(self, *args, **kwargs) + self._invalidate_cache() + return rv + + return inner_func + + +class rrulebase(object): + def __init__(self, cache=False): + if cache: + self._cache = [] + self._cache_lock = _thread.allocate_lock() + self._invalidate_cache() + else: + self._cache = None + self._cache_complete = False + self._len = None + + def __iter__(self): + if self._cache_complete: + return iter(self._cache) + elif self._cache is None: + return self._iter() + else: + return self._iter_cached() + + def _invalidate_cache(self): + if self._cache is not None: + self._cache = [] + self._cache_complete = False + self._cache_gen = self._iter() + + if self._cache_lock.locked(): + self._cache_lock.release() + + self._len = None + + def _iter_cached(self): + i = 0 + gen = self._cache_gen + cache = self._cache + acquire = self._cache_lock.acquire + release = self._cache_lock.release + while gen: + if i == len(cache): + acquire() + if self._cache_complete: + break + try: + for j in range(10): + cache.append(advance_iterator(gen)) + except StopIteration: + self._cache_gen = gen = None + self._cache_complete = True + break + release() + yield cache[i] + i += 1 + while i < self._len: + yield cache[i] + i += 1 + + def __getitem__(self, item): + if self._cache_complete: + return self._cache[item] + elif isinstance(item, slice): + if item.step and item.step < 0: + return list(iter(self))[item] + else: + return list(itertools.islice(self, + item.start or 0, + item.stop or sys.maxsize, + item.step or 1)) + elif item >= 0: + gen = iter(self) + try: + for i in range(item+1): + res = advance_iterator(gen) + except StopIteration: + raise IndexError + return res + else: + return list(iter(self))[item] + + def __contains__(self, item): + if self._cache_complete: + return item in self._cache + else: + for i in self: + if i == item: + return True + elif i > item: + return False + return False + + # __len__() introduces a large performance penalty. + def count(self): + """ Returns the number of recurrences in this set. It will have go + through the whole recurrence, if this hasn't been done before. """ + if self._len is None: + for x in self: + pass + return self._len + + def before(self, dt, inc=False): + """ Returns the last recurrence before the given datetime instance. The + inc keyword defines what happens if dt is an occurrence. With + inc=True, if dt itself is an occurrence, it will be returned. """ + if self._cache_complete: + gen = self._cache + else: + gen = self + last = None + if inc: + for i in gen: + if i > dt: + break + last = i + else: + for i in gen: + if i >= dt: + break + last = i + return last + + def after(self, dt, inc=False): + """ Returns the first recurrence after the given datetime instance. The + inc keyword defines what happens if dt is an occurrence. With + inc=True, if dt itself is an occurrence, it will be returned. """ + if self._cache_complete: + gen = self._cache + else: + gen = self + if inc: + for i in gen: + if i >= dt: + return i + else: + for i in gen: + if i > dt: + return i + return None + + def xafter(self, dt, count=None, inc=False): + """ + Generator which yields up to `count` recurrences after the given + datetime instance, equivalent to `after`. + + :param dt: + The datetime at which to start generating recurrences. + + :param count: + The maximum number of recurrences to generate. If `None` (default), + dates are generated until the recurrence rule is exhausted. + + :param inc: + If `dt` is an instance of the rule and `inc` is `True`, it is + included in the output. + + :yields: Yields a sequence of `datetime` objects. + """ + + if self._cache_complete: + gen = self._cache + else: + gen = self + + # Select the comparison function + if inc: + comp = lambda dc, dtc: dc >= dtc + else: + comp = lambda dc, dtc: dc > dtc + + # Generate dates + n = 0 + for d in gen: + if comp(d, dt): + if count is not None: + n += 1 + if n > count: + break + + yield d + + def between(self, after, before, inc=False, count=1): + """ Returns all the occurrences of the rrule between after and before. + The inc keyword defines what happens if after and/or before are + themselves occurrences. With inc=True, they will be included in the + list, if they are found in the recurrence set. """ + if self._cache_complete: + gen = self._cache + else: + gen = self + started = False + l = [] + if inc: + for i in gen: + if i > before: + break + elif not started: + if i >= after: + started = True + l.append(i) + else: + l.append(i) + else: + for i in gen: + if i >= before: + break + elif not started: + if i > after: + started = True + l.append(i) + else: + l.append(i) + return l + + +class rrule(rrulebase): + """ + That's the base of the rrule operation. It accepts all the keywords + defined in the RFC as its constructor parameters (except byday, + which was renamed to byweekday) and more. The constructor prototype is:: + + rrule(freq) + + Where freq must be one of YEARLY, MONTHLY, WEEKLY, DAILY, HOURLY, MINUTELY, + or SECONDLY. + + .. note:: + Per RFC section 3.3.10, recurrence instances falling on invalid dates + and times are ignored rather than coerced: + + Recurrence rules may generate recurrence instances with an invalid + date (e.g., February 30) or nonexistent local time (e.g., 1:30 AM + on a day where the local time is moved forward by an hour at 1:00 + AM). Such recurrence instances MUST be ignored and MUST NOT be + counted as part of the recurrence set. + + This can lead to possibly surprising behavior when, for example, the + start date occurs at the end of the month: + + >>> from dateutil.rrule import rrule, MONTHLY + >>> from datetime import datetime + >>> start_date = datetime(2014, 12, 31) + >>> list(rrule(freq=MONTHLY, count=4, dtstart=start_date)) + ... # doctest: +NORMALIZE_WHITESPACE + [datetime.datetime(2014, 12, 31, 0, 0), + datetime.datetime(2015, 1, 31, 0, 0), + datetime.datetime(2015, 3, 31, 0, 0), + datetime.datetime(2015, 5, 31, 0, 0)] + + Additionally, it supports the following keyword arguments: + + :param dtstart: + The recurrence start. Besides being the base for the recurrence, + missing parameters in the final recurrence instances will also be + extracted from this date. If not given, datetime.now() will be used + instead. + :param interval: + The interval between each freq iteration. For example, when using + YEARLY, an interval of 2 means once every two years, but with HOURLY, + it means once every two hours. The default interval is 1. + :param wkst: + The week start day. Must be one of the MO, TU, WE constants, or an + integer, specifying the first day of the week. This will affect + recurrences based on weekly periods. The default week start is got + from calendar.firstweekday(), and may be modified by + calendar.setfirstweekday(). + :param count: + If given, this determines how many occurrences will be generated. + + .. note:: + As of version 2.5.0, the use of the keyword ``until`` in conjunction + with ``count`` is deprecated, to make sure ``dateutil`` is fully + compliant with `RFC-5545 Sec. 3.3.10 `_. Therefore, ``until`` and ``count`` + **must not** occur in the same call to ``rrule``. + :param until: + If given, this must be a datetime instance specifying the upper-bound + limit of the recurrence. The last recurrence in the rule is the greatest + datetime that is less than or equal to the value specified in the + ``until`` parameter. + + .. note:: + As of version 2.5.0, the use of the keyword ``until`` in conjunction + with ``count`` is deprecated, to make sure ``dateutil`` is fully + compliant with `RFC-5545 Sec. 3.3.10 `_. Therefore, ``until`` and ``count`` + **must not** occur in the same call to ``rrule``. + :param bysetpos: + If given, it must be either an integer, or a sequence of integers, + positive or negative. Each given integer will specify an occurrence + number, corresponding to the nth occurrence of the rule inside the + frequency period. For example, a bysetpos of -1 if combined with a + MONTHLY frequency, and a byweekday of (MO, TU, WE, TH, FR), will + result in the last work day of every month. + :param bymonth: + If given, it must be either an integer, or a sequence of integers, + meaning the months to apply the recurrence to. + :param bymonthday: + If given, it must be either an integer, or a sequence of integers, + meaning the month days to apply the recurrence to. + :param byyearday: + If given, it must be either an integer, or a sequence of integers, + meaning the year days to apply the recurrence to. + :param byeaster: + If given, it must be either an integer, or a sequence of integers, + positive or negative. Each integer will define an offset from the + Easter Sunday. Passing the offset 0 to byeaster will yield the Easter + Sunday itself. This is an extension to the RFC specification. + :param byweekno: + If given, it must be either an integer, or a sequence of integers, + meaning the week numbers to apply the recurrence to. Week numbers + have the meaning described in ISO8601, that is, the first week of + the year is that containing at least four days of the new year. + :param byweekday: + If given, it must be either an integer (0 == MO), a sequence of + integers, one of the weekday constants (MO, TU, etc), or a sequence + of these constants. When given, these variables will define the + weekdays where the recurrence will be applied. It's also possible to + use an argument n for the weekday instances, which will mean the nth + occurrence of this weekday in the period. For example, with MONTHLY, + or with YEARLY and BYMONTH, using FR(+1) in byweekday will specify the + first friday of the month where the recurrence happens. Notice that in + the RFC documentation, this is specified as BYDAY, but was renamed to + avoid the ambiguity of that keyword. + :param byhour: + If given, it must be either an integer, or a sequence of integers, + meaning the hours to apply the recurrence to. + :param byminute: + If given, it must be either an integer, or a sequence of integers, + meaning the minutes to apply the recurrence to. + :param bysecond: + If given, it must be either an integer, or a sequence of integers, + meaning the seconds to apply the recurrence to. + :param cache: + If given, it must be a boolean value specifying to enable or disable + caching of results. If you will use the same rrule instance multiple + times, enabling caching will improve the performance considerably. + """ + def __init__(self, freq, dtstart=None, + interval=1, wkst=None, count=None, until=None, bysetpos=None, + bymonth=None, bymonthday=None, byyearday=None, byeaster=None, + byweekno=None, byweekday=None, + byhour=None, byminute=None, bysecond=None, + cache=False): + super(rrule, self).__init__(cache) + global easter + if not dtstart: + if until and until.tzinfo: + dtstart = datetime.datetime.now(tz=until.tzinfo).replace(microsecond=0) + else: + dtstart = datetime.datetime.now().replace(microsecond=0) + elif not isinstance(dtstart, datetime.datetime): + dtstart = datetime.datetime.fromordinal(dtstart.toordinal()) + else: + dtstart = dtstart.replace(microsecond=0) + self._dtstart = dtstart + self._tzinfo = dtstart.tzinfo + self._freq = freq + self._interval = interval + self._count = count + + # Cache the original byxxx rules, if they are provided, as the _byxxx + # attributes do not necessarily map to the inputs, and this can be + # a problem in generating the strings. Only store things if they've + # been supplied (the string retrieval will just use .get()) + self._original_rule = {} + + if until and not isinstance(until, datetime.datetime): + until = datetime.datetime.fromordinal(until.toordinal()) + self._until = until + + if self._dtstart and self._until: + if (self._dtstart.tzinfo is not None) != (self._until.tzinfo is not None): + # According to RFC5545 Section 3.3.10: + # https://tools.ietf.org/html/rfc5545#section-3.3.10 + # + # > If the "DTSTART" property is specified as a date with UTC + # > time or a date with local time and time zone reference, + # > then the UNTIL rule part MUST be specified as a date with + # > UTC time. + raise ValueError( + 'RRULE UNTIL values must be specified in UTC when DTSTART ' + 'is timezone-aware' + ) + + if count is not None and until: + warn("Using both 'count' and 'until' is inconsistent with RFC 5545" + " and has been deprecated in dateutil. Future versions will " + "raise an error.", DeprecationWarning) + + if wkst is None: + self._wkst = calendar.firstweekday() + elif isinstance(wkst, integer_types): + self._wkst = wkst + else: + self._wkst = wkst.weekday + + if bysetpos is None: + self._bysetpos = None + elif isinstance(bysetpos, integer_types): + if bysetpos == 0 or not (-366 <= bysetpos <= 366): + raise ValueError("bysetpos must be between 1 and 366, " + "or between -366 and -1") + self._bysetpos = (bysetpos,) + else: + self._bysetpos = tuple(bysetpos) + for pos in self._bysetpos: + if pos == 0 or not (-366 <= pos <= 366): + raise ValueError("bysetpos must be between 1 and 366, " + "or between -366 and -1") + + if self._bysetpos: + self._original_rule['bysetpos'] = self._bysetpos + + if (byweekno is None and byyearday is None and bymonthday is None and + byweekday is None and byeaster is None): + if freq == YEARLY: + if bymonth is None: + bymonth = dtstart.month + self._original_rule['bymonth'] = None + bymonthday = dtstart.day + self._original_rule['bymonthday'] = None + elif freq == MONTHLY: + bymonthday = dtstart.day + self._original_rule['bymonthday'] = None + elif freq == WEEKLY: + byweekday = dtstart.weekday() + self._original_rule['byweekday'] = None + + # bymonth + if bymonth is None: + self._bymonth = None + else: + if isinstance(bymonth, integer_types): + bymonth = (bymonth,) + + self._bymonth = tuple(sorted(set(bymonth))) + + if 'bymonth' not in self._original_rule: + self._original_rule['bymonth'] = self._bymonth + + # byyearday + if byyearday is None: + self._byyearday = None + else: + if isinstance(byyearday, integer_types): + byyearday = (byyearday,) + + self._byyearday = tuple(sorted(set(byyearday))) + self._original_rule['byyearday'] = self._byyearday + + # byeaster + if byeaster is not None: + if not easter: + from dateutil import easter + if isinstance(byeaster, integer_types): + self._byeaster = (byeaster,) + else: + self._byeaster = tuple(sorted(byeaster)) + + self._original_rule['byeaster'] = self._byeaster + else: + self._byeaster = None + + # bymonthday + if bymonthday is None: + self._bymonthday = () + self._bynmonthday = () + else: + if isinstance(bymonthday, integer_types): + bymonthday = (bymonthday,) + + bymonthday = set(bymonthday) # Ensure it's unique + + self._bymonthday = tuple(sorted(x for x in bymonthday if x > 0)) + self._bynmonthday = tuple(sorted(x for x in bymonthday if x < 0)) + + # Storing positive numbers first, then negative numbers + if 'bymonthday' not in self._original_rule: + self._original_rule['bymonthday'] = tuple( + itertools.chain(self._bymonthday, self._bynmonthday)) + + # byweekno + if byweekno is None: + self._byweekno = None + else: + if isinstance(byweekno, integer_types): + byweekno = (byweekno,) + + self._byweekno = tuple(sorted(set(byweekno))) + + self._original_rule['byweekno'] = self._byweekno + + # byweekday / bynweekday + if byweekday is None: + self._byweekday = None + self._bynweekday = None + else: + # If it's one of the valid non-sequence types, convert to a + # single-element sequence before the iterator that builds the + # byweekday set. + if isinstance(byweekday, integer_types) or hasattr(byweekday, "n"): + byweekday = (byweekday,) + + self._byweekday = set() + self._bynweekday = set() + for wday in byweekday: + if isinstance(wday, integer_types): + self._byweekday.add(wday) + elif not wday.n or freq > MONTHLY: + self._byweekday.add(wday.weekday) + else: + self._bynweekday.add((wday.weekday, wday.n)) + + if not self._byweekday: + self._byweekday = None + elif not self._bynweekday: + self._bynweekday = None + + if self._byweekday is not None: + self._byweekday = tuple(sorted(self._byweekday)) + orig_byweekday = [weekday(x) for x in self._byweekday] + else: + orig_byweekday = () + + if self._bynweekday is not None: + self._bynweekday = tuple(sorted(self._bynweekday)) + orig_bynweekday = [weekday(*x) for x in self._bynweekday] + else: + orig_bynweekday = () + + if 'byweekday' not in self._original_rule: + self._original_rule['byweekday'] = tuple(itertools.chain( + orig_byweekday, orig_bynweekday)) + + # byhour + if byhour is None: + if freq < HOURLY: + self._byhour = {dtstart.hour} + else: + self._byhour = None + else: + if isinstance(byhour, integer_types): + byhour = (byhour,) + + if freq == HOURLY: + self._byhour = self.__construct_byset(start=dtstart.hour, + byxxx=byhour, + base=24) + else: + self._byhour = set(byhour) + + self._byhour = tuple(sorted(self._byhour)) + self._original_rule['byhour'] = self._byhour + + # byminute + if byminute is None: + if freq < MINUTELY: + self._byminute = {dtstart.minute} + else: + self._byminute = None + else: + if isinstance(byminute, integer_types): + byminute = (byminute,) + + if freq == MINUTELY: + self._byminute = self.__construct_byset(start=dtstart.minute, + byxxx=byminute, + base=60) + else: + self._byminute = set(byminute) + + self._byminute = tuple(sorted(self._byminute)) + self._original_rule['byminute'] = self._byminute + + # bysecond + if bysecond is None: + if freq < SECONDLY: + self._bysecond = ((dtstart.second,)) + else: + self._bysecond = None + else: + if isinstance(bysecond, integer_types): + bysecond = (bysecond,) + + self._bysecond = set(bysecond) + + if freq == SECONDLY: + self._bysecond = self.__construct_byset(start=dtstart.second, + byxxx=bysecond, + base=60) + else: + self._bysecond = set(bysecond) + + self._bysecond = tuple(sorted(self._bysecond)) + self._original_rule['bysecond'] = self._bysecond + + if self._freq >= HOURLY: + self._timeset = None + else: + self._timeset = [] + for hour in self._byhour: + for minute in self._byminute: + for second in self._bysecond: + self._timeset.append( + datetime.time(hour, minute, second, + tzinfo=self._tzinfo)) + self._timeset.sort() + self._timeset = tuple(self._timeset) + + def __str__(self): + """ + Output a string that would generate this RRULE if passed to rrulestr. + This is mostly compatible with RFC5545, except for the + dateutil-specific extension BYEASTER. + """ + + output = [] + h, m, s = [None] * 3 + if self._dtstart: + output.append(self._dtstart.strftime('DTSTART:%Y%m%dT%H%M%S')) + h, m, s = self._dtstart.timetuple()[3:6] + + parts = ['FREQ=' + FREQNAMES[self._freq]] + if self._interval != 1: + parts.append('INTERVAL=' + str(self._interval)) + + if self._wkst: + parts.append('WKST=' + repr(weekday(self._wkst))[0:2]) + + if self._count is not None: + parts.append('COUNT=' + str(self._count)) + + if self._until: + parts.append(self._until.strftime('UNTIL=%Y%m%dT%H%M%S')) + + if self._original_rule.get('byweekday') is not None: + # The str() method on weekday objects doesn't generate + # RFC5545-compliant strings, so we should modify that. + original_rule = dict(self._original_rule) + wday_strings = [] + for wday in original_rule['byweekday']: + if wday.n: + wday_strings.append('{n:+d}{wday}'.format( + n=wday.n, + wday=repr(wday)[0:2])) + else: + wday_strings.append(repr(wday)) + + original_rule['byweekday'] = wday_strings + else: + original_rule = self._original_rule + + partfmt = '{name}={vals}' + for name, key in [('BYSETPOS', 'bysetpos'), + ('BYMONTH', 'bymonth'), + ('BYMONTHDAY', 'bymonthday'), + ('BYYEARDAY', 'byyearday'), + ('BYWEEKNO', 'byweekno'), + ('BYDAY', 'byweekday'), + ('BYHOUR', 'byhour'), + ('BYMINUTE', 'byminute'), + ('BYSECOND', 'bysecond'), + ('BYEASTER', 'byeaster')]: + value = original_rule.get(key) + if value: + parts.append(partfmt.format(name=name, vals=(','.join(str(v) + for v in value)))) + + output.append('RRULE:' + ';'.join(parts)) + return '\n'.join(output) + + def replace(self, **kwargs): + """Return new rrule with same attributes except for those attributes given new + values by whichever keyword arguments are specified.""" + new_kwargs = {"interval": self._interval, + "count": self._count, + "dtstart": self._dtstart, + "freq": self._freq, + "until": self._until, + "wkst": self._wkst, + "cache": False if self._cache is None else True } + new_kwargs.update(self._original_rule) + new_kwargs.update(kwargs) + return rrule(**new_kwargs) + + def _iter(self): + year, month, day, hour, minute, second, weekday, yearday, _ = \ + self._dtstart.timetuple() + + # Some local variables to speed things up a bit + freq = self._freq + interval = self._interval + wkst = self._wkst + until = self._until + bymonth = self._bymonth + byweekno = self._byweekno + byyearday = self._byyearday + byweekday = self._byweekday + byeaster = self._byeaster + bymonthday = self._bymonthday + bynmonthday = self._bynmonthday + bysetpos = self._bysetpos + byhour = self._byhour + byminute = self._byminute + bysecond = self._bysecond + + ii = _iterinfo(self) + ii.rebuild(year, month) + + getdayset = {YEARLY: ii.ydayset, + MONTHLY: ii.mdayset, + WEEKLY: ii.wdayset, + DAILY: ii.ddayset, + HOURLY: ii.ddayset, + MINUTELY: ii.ddayset, + SECONDLY: ii.ddayset}[freq] + + if freq < HOURLY: + timeset = self._timeset + else: + gettimeset = {HOURLY: ii.htimeset, + MINUTELY: ii.mtimeset, + SECONDLY: ii.stimeset}[freq] + if ((freq >= HOURLY and + self._byhour and hour not in self._byhour) or + (freq >= MINUTELY and + self._byminute and minute not in self._byminute) or + (freq >= SECONDLY and + self._bysecond and second not in self._bysecond)): + timeset = () + else: + timeset = gettimeset(hour, minute, second) + + total = 0 + count = self._count + while True: + # Get dayset with the right frequency + dayset, start, end = getdayset(year, month, day) + + # Do the "hard" work ;-) + filtered = False + for i in dayset[start:end]: + if ((bymonth and ii.mmask[i] not in bymonth) or + (byweekno and not ii.wnomask[i]) or + (byweekday and ii.wdaymask[i] not in byweekday) or + (ii.nwdaymask and not ii.nwdaymask[i]) or + (byeaster and not ii.eastermask[i]) or + ((bymonthday or bynmonthday) and + ii.mdaymask[i] not in bymonthday and + ii.nmdaymask[i] not in bynmonthday) or + (byyearday and + ((i < ii.yearlen and i+1 not in byyearday and + -ii.yearlen+i not in byyearday) or + (i >= ii.yearlen and i+1-ii.yearlen not in byyearday and + -ii.nextyearlen+i-ii.yearlen not in byyearday)))): + dayset[i] = None + filtered = True + + # Output results + if bysetpos and timeset: + poslist = [] + for pos in bysetpos: + if pos < 0: + daypos, timepos = divmod(pos, len(timeset)) + else: + daypos, timepos = divmod(pos-1, len(timeset)) + try: + i = [x for x in dayset[start:end] + if x is not None][daypos] + time = timeset[timepos] + except IndexError: + pass + else: + date = datetime.date.fromordinal(ii.yearordinal+i) + res = datetime.datetime.combine(date, time) + if res not in poslist: + poslist.append(res) + poslist.sort() + for res in poslist: + if until and res > until: + self._len = total + return + elif res >= self._dtstart: + if count is not None: + count -= 1 + if count < 0: + self._len = total + return + total += 1 + yield res + else: + for i in dayset[start:end]: + if i is not None: + date = datetime.date.fromordinal(ii.yearordinal + i) + for time in timeset: + res = datetime.datetime.combine(date, time) + if until and res > until: + self._len = total + return + elif res >= self._dtstart: + if count is not None: + count -= 1 + if count < 0: + self._len = total + return + + total += 1 + yield res + + # Handle frequency and interval + fixday = False + if freq == YEARLY: + year += interval + if year > datetime.MAXYEAR: + self._len = total + return + ii.rebuild(year, month) + elif freq == MONTHLY: + month += interval + if month > 12: + div, mod = divmod(month, 12) + month = mod + year += div + if month == 0: + month = 12 + year -= 1 + if year > datetime.MAXYEAR: + self._len = total + return + ii.rebuild(year, month) + elif freq == WEEKLY: + if wkst > weekday: + day += -(weekday+1+(6-wkst))+self._interval*7 + else: + day += -(weekday-wkst)+self._interval*7 + weekday = wkst + fixday = True + elif freq == DAILY: + day += interval + fixday = True + elif freq == HOURLY: + if filtered: + # Jump to one iteration before next day + hour += ((23-hour)//interval)*interval + + if byhour: + ndays, hour = self.__mod_distance(value=hour, + byxxx=self._byhour, + base=24) + else: + ndays, hour = divmod(hour+interval, 24) + + if ndays: + day += ndays + fixday = True + + timeset = gettimeset(hour, minute, second) + elif freq == MINUTELY: + if filtered: + # Jump to one iteration before next day + minute += ((1439-(hour*60+minute))//interval)*interval + + valid = False + rep_rate = (24*60) + for j in range(rep_rate // gcd(interval, rep_rate)): + if byminute: + nhours, minute = \ + self.__mod_distance(value=minute, + byxxx=self._byminute, + base=60) + else: + nhours, minute = divmod(minute+interval, 60) + + div, hour = divmod(hour+nhours, 24) + if div: + day += div + fixday = True + filtered = False + + if not byhour or hour in byhour: + valid = True + break + + if not valid: + raise ValueError('Invalid combination of interval and ' + + 'byhour resulting in empty rule.') + + timeset = gettimeset(hour, minute, second) + elif freq == SECONDLY: + if filtered: + # Jump to one iteration before next day + second += (((86399 - (hour * 3600 + minute * 60 + second)) + // interval) * interval) + + rep_rate = (24 * 3600) + valid = False + for j in range(0, rep_rate // gcd(interval, rep_rate)): + if bysecond: + nminutes, second = \ + self.__mod_distance(value=second, + byxxx=self._bysecond, + base=60) + else: + nminutes, second = divmod(second+interval, 60) + + div, minute = divmod(minute+nminutes, 60) + if div: + hour += div + div, hour = divmod(hour, 24) + if div: + day += div + fixday = True + + if ((not byhour or hour in byhour) and + (not byminute or minute in byminute) and + (not bysecond or second in bysecond)): + valid = True + break + + if not valid: + raise ValueError('Invalid combination of interval, ' + + 'byhour and byminute resulting in empty' + + ' rule.') + + timeset = gettimeset(hour, minute, second) + + if fixday and day > 28: + daysinmonth = calendar.monthrange(year, month)[1] + if day > daysinmonth: + while day > daysinmonth: + day -= daysinmonth + month += 1 + if month == 13: + month = 1 + year += 1 + if year > datetime.MAXYEAR: + self._len = total + return + daysinmonth = calendar.monthrange(year, month)[1] + ii.rebuild(year, month) + + def __construct_byset(self, start, byxxx, base): + """ + If a `BYXXX` sequence is passed to the constructor at the same level as + `FREQ` (e.g. `FREQ=HOURLY,BYHOUR={2,4,7},INTERVAL=3`), there are some + specifications which cannot be reached given some starting conditions. + + This occurs whenever the interval is not coprime with the base of a + given unit and the difference between the starting position and the + ending position is not coprime with the greatest common denominator + between the interval and the base. For example, with a FREQ of hourly + starting at 17:00 and an interval of 4, the only valid values for + BYHOUR would be {21, 1, 5, 9, 13, 17}, because 4 and 24 are not + coprime. + + :param start: + Specifies the starting position. + :param byxxx: + An iterable containing the list of allowed values. + :param base: + The largest allowable value for the specified frequency (e.g. + 24 hours, 60 minutes). + + This does not preserve the type of the iterable, returning a set, since + the values should be unique and the order is irrelevant, this will + speed up later lookups. + + In the event of an empty set, raises a :exception:`ValueError`, as this + results in an empty rrule. + """ + + cset = set() + + # Support a single byxxx value. + if isinstance(byxxx, integer_types): + byxxx = (byxxx, ) + + for num in byxxx: + i_gcd = gcd(self._interval, base) + # Use divmod rather than % because we need to wrap negative nums. + if i_gcd == 1 or divmod(num - start, i_gcd)[1] == 0: + cset.add(num) + + if len(cset) == 0: + raise ValueError("Invalid rrule byxxx generates an empty set.") + + return cset + + def __mod_distance(self, value, byxxx, base): + """ + Calculates the next value in a sequence where the `FREQ` parameter is + specified along with a `BYXXX` parameter at the same "level" + (e.g. `HOURLY` specified with `BYHOUR`). + + :param value: + The old value of the component. + :param byxxx: + The `BYXXX` set, which should have been generated by + `rrule._construct_byset`, or something else which checks that a + valid rule is present. + :param base: + The largest allowable value for the specified frequency (e.g. + 24 hours, 60 minutes). + + If a valid value is not found after `base` iterations (the maximum + number before the sequence would start to repeat), this raises a + :exception:`ValueError`, as no valid values were found. + + This returns a tuple of `divmod(n*interval, base)`, where `n` is the + smallest number of `interval` repetitions until the next specified + value in `byxxx` is found. + """ + accumulator = 0 + for ii in range(1, base + 1): + # Using divmod() over % to account for negative intervals + div, value = divmod(value + self._interval, base) + accumulator += div + if value in byxxx: + return (accumulator, value) + + +class _iterinfo(object): + __slots__ = ["rrule", "lastyear", "lastmonth", + "yearlen", "nextyearlen", "yearordinal", "yearweekday", + "mmask", "mrange", "mdaymask", "nmdaymask", + "wdaymask", "wnomask", "nwdaymask", "eastermask"] + + def __init__(self, rrule): + for attr in self.__slots__: + setattr(self, attr, None) + self.rrule = rrule + + def rebuild(self, year, month): + # Every mask is 7 days longer to handle cross-year weekly periods. + rr = self.rrule + if year != self.lastyear: + self.yearlen = 365 + calendar.isleap(year) + self.nextyearlen = 365 + calendar.isleap(year + 1) + firstyday = datetime.date(year, 1, 1) + self.yearordinal = firstyday.toordinal() + self.yearweekday = firstyday.weekday() + + wday = datetime.date(year, 1, 1).weekday() + if self.yearlen == 365: + self.mmask = M365MASK + self.mdaymask = MDAY365MASK + self.nmdaymask = NMDAY365MASK + self.wdaymask = WDAYMASK[wday:] + self.mrange = M365RANGE + else: + self.mmask = M366MASK + self.mdaymask = MDAY366MASK + self.nmdaymask = NMDAY366MASK + self.wdaymask = WDAYMASK[wday:] + self.mrange = M366RANGE + + if not rr._byweekno: + self.wnomask = None + else: + self.wnomask = [0]*(self.yearlen+7) + # no1wkst = firstwkst = self.wdaymask.index(rr._wkst) + no1wkst = firstwkst = (7-self.yearweekday+rr._wkst) % 7 + if no1wkst >= 4: + no1wkst = 0 + # Number of days in the year, plus the days we got + # from last year. + wyearlen = self.yearlen+(self.yearweekday-rr._wkst) % 7 + else: + # Number of days in the year, minus the days we + # left in last year. + wyearlen = self.yearlen-no1wkst + div, mod = divmod(wyearlen, 7) + numweeks = div+mod//4 + for n in rr._byweekno: + if n < 0: + n += numweeks+1 + if not (0 < n <= numweeks): + continue + if n > 1: + i = no1wkst+(n-1)*7 + if no1wkst != firstwkst: + i -= 7-firstwkst + else: + i = no1wkst + for j in range(7): + self.wnomask[i] = 1 + i += 1 + if self.wdaymask[i] == rr._wkst: + break + if 1 in rr._byweekno: + # Check week number 1 of next year as well + # TODO: Check -numweeks for next year. + i = no1wkst+numweeks*7 + if no1wkst != firstwkst: + i -= 7-firstwkst + if i < self.yearlen: + # If week starts in next year, we + # don't care about it. + for j in range(7): + self.wnomask[i] = 1 + i += 1 + if self.wdaymask[i] == rr._wkst: + break + if no1wkst: + # Check last week number of last year as + # well. If no1wkst is 0, either the year + # started on week start, or week number 1 + # got days from last year, so there are no + # days from last year's last week number in + # this year. + if -1 not in rr._byweekno: + lyearweekday = datetime.date(year-1, 1, 1).weekday() + lno1wkst = (7-lyearweekday+rr._wkst) % 7 + lyearlen = 365+calendar.isleap(year-1) + if lno1wkst >= 4: + lno1wkst = 0 + lnumweeks = 52+(lyearlen + + (lyearweekday-rr._wkst) % 7) % 7//4 + else: + lnumweeks = 52+(self.yearlen-no1wkst) % 7//4 + else: + lnumweeks = -1 + if lnumweeks in rr._byweekno: + for i in range(no1wkst): + self.wnomask[i] = 1 + + if (rr._bynweekday and (month != self.lastmonth or + year != self.lastyear)): + ranges = [] + if rr._freq == YEARLY: + if rr._bymonth: + for month in rr._bymonth: + ranges.append(self.mrange[month-1:month+1]) + else: + ranges = [(0, self.yearlen)] + elif rr._freq == MONTHLY: + ranges = [self.mrange[month-1:month+1]] + if ranges: + # Weekly frequency won't get here, so we may not + # care about cross-year weekly periods. + self.nwdaymask = [0]*self.yearlen + for first, last in ranges: + last -= 1 + for wday, n in rr._bynweekday: + if n < 0: + i = last+(n+1)*7 + i -= (self.wdaymask[i]-wday) % 7 + else: + i = first+(n-1)*7 + i += (7-self.wdaymask[i]+wday) % 7 + if first <= i <= last: + self.nwdaymask[i] = 1 + + if rr._byeaster: + self.eastermask = [0]*(self.yearlen+7) + eyday = easter.easter(year).toordinal()-self.yearordinal + for offset in rr._byeaster: + self.eastermask[eyday+offset] = 1 + + self.lastyear = year + self.lastmonth = month + + def ydayset(self, year, month, day): + return list(range(self.yearlen)), 0, self.yearlen + + def mdayset(self, year, month, day): + dset = [None]*self.yearlen + start, end = self.mrange[month-1:month+1] + for i in range(start, end): + dset[i] = i + return dset, start, end + + def wdayset(self, year, month, day): + # We need to handle cross-year weeks here. + dset = [None]*(self.yearlen+7) + i = datetime.date(year, month, day).toordinal()-self.yearordinal + start = i + for j in range(7): + dset[i] = i + i += 1 + # if (not (0 <= i < self.yearlen) or + # self.wdaymask[i] == self.rrule._wkst): + # This will cross the year boundary, if necessary. + if self.wdaymask[i] == self.rrule._wkst: + break + return dset, start, i + + def ddayset(self, year, month, day): + dset = [None] * self.yearlen + i = datetime.date(year, month, day).toordinal() - self.yearordinal + dset[i] = i + return dset, i, i + 1 + + def htimeset(self, hour, minute, second): + tset = [] + rr = self.rrule + for minute in rr._byminute: + for second in rr._bysecond: + tset.append(datetime.time(hour, minute, second, + tzinfo=rr._tzinfo)) + tset.sort() + return tset + + def mtimeset(self, hour, minute, second): + tset = [] + rr = self.rrule + for second in rr._bysecond: + tset.append(datetime.time(hour, minute, second, tzinfo=rr._tzinfo)) + tset.sort() + return tset + + def stimeset(self, hour, minute, second): + return (datetime.time(hour, minute, second, + tzinfo=self.rrule._tzinfo),) + + +class rruleset(rrulebase): + """ The rruleset type allows more complex recurrence setups, mixing + multiple rules, dates, exclusion rules, and exclusion dates. The type + constructor takes the following keyword arguments: + + :param cache: If True, caching of results will be enabled, improving + performance of multiple queries considerably. """ + + class _genitem(object): + def __init__(self, genlist, gen): + try: + self.dt = advance_iterator(gen) + genlist.append(self) + except StopIteration: + pass + self.genlist = genlist + self.gen = gen + + def __next__(self): + try: + self.dt = advance_iterator(self.gen) + except StopIteration: + if self.genlist[0] is self: + heapq.heappop(self.genlist) + else: + self.genlist.remove(self) + heapq.heapify(self.genlist) + + next = __next__ + + def __lt__(self, other): + return self.dt < other.dt + + def __gt__(self, other): + return self.dt > other.dt + + def __eq__(self, other): + return self.dt == other.dt + + def __ne__(self, other): + return self.dt != other.dt + + def __init__(self, cache=False): + super(rruleset, self).__init__(cache) + self._rrule = [] + self._rdate = [] + self._exrule = [] + self._exdate = [] + + @_invalidates_cache + def rrule(self, rrule): + """ Include the given :py:class:`rrule` instance in the recurrence set + generation. """ + self._rrule.append(rrule) + + @_invalidates_cache + def rdate(self, rdate): + """ Include the given :py:class:`datetime` instance in the recurrence + set generation. """ + self._rdate.append(rdate) + + @_invalidates_cache + def exrule(self, exrule): + """ Include the given rrule instance in the recurrence set exclusion + list. Dates which are part of the given recurrence rules will not + be generated, even if some inclusive rrule or rdate matches them. + """ + self._exrule.append(exrule) + + @_invalidates_cache + def exdate(self, exdate): + """ Include the given datetime instance in the recurrence set + exclusion list. Dates included that way will not be generated, + even if some inclusive rrule or rdate matches them. """ + self._exdate.append(exdate) + + def _iter(self): + rlist = [] + self._rdate.sort() + self._genitem(rlist, iter(self._rdate)) + for gen in [iter(x) for x in self._rrule]: + self._genitem(rlist, gen) + exlist = [] + self._exdate.sort() + self._genitem(exlist, iter(self._exdate)) + for gen in [iter(x) for x in self._exrule]: + self._genitem(exlist, gen) + lastdt = None + total = 0 + heapq.heapify(rlist) + heapq.heapify(exlist) + while rlist: + ritem = rlist[0] + if not lastdt or lastdt != ritem.dt: + while exlist and exlist[0] < ritem: + exitem = exlist[0] + advance_iterator(exitem) + if exlist and exlist[0] is exitem: + heapq.heapreplace(exlist, exitem) + if not exlist or ritem != exlist[0]: + total += 1 + yield ritem.dt + lastdt = ritem.dt + advance_iterator(ritem) + if rlist and rlist[0] is ritem: + heapq.heapreplace(rlist, ritem) + self._len = total + + + + +class _rrulestr(object): + """ Parses a string representation of a recurrence rule or set of + recurrence rules. + + :param s: + Required, a string defining one or more recurrence rules. + + :param dtstart: + If given, used as the default recurrence start if not specified in the + rule string. + + :param cache: + If set ``True`` caching of results will be enabled, improving + performance of multiple queries considerably. + + :param unfold: + If set ``True`` indicates that a rule string is split over more + than one line and should be joined before processing. + + :param forceset: + If set ``True`` forces a :class:`dateutil.rrule.rruleset` to + be returned. + + :param compatible: + If set ``True`` forces ``unfold`` and ``forceset`` to be ``True``. + + :param ignoretz: + If set ``True``, time zones in parsed strings are ignored and a naive + :class:`datetime.datetime` object is returned. + + :param tzids: + If given, a callable or mapping used to retrieve a + :class:`datetime.tzinfo` from a string representation. + Defaults to :func:`dateutil.tz.gettz`. + + :param tzinfos: + Additional time zone names / aliases which may be present in a string + representation. See :func:`dateutil.parser.parse` for more + information. + + :return: + Returns a :class:`dateutil.rrule.rruleset` or + :class:`dateutil.rrule.rrule` + """ + + _freq_map = {"YEARLY": YEARLY, + "MONTHLY": MONTHLY, + "WEEKLY": WEEKLY, + "DAILY": DAILY, + "HOURLY": HOURLY, + "MINUTELY": MINUTELY, + "SECONDLY": SECONDLY} + + _weekday_map = {"MO": 0, "TU": 1, "WE": 2, "TH": 3, + "FR": 4, "SA": 5, "SU": 6} + + def _handle_int(self, rrkwargs, name, value, **kwargs): + rrkwargs[name.lower()] = int(value) + + def _handle_int_list(self, rrkwargs, name, value, **kwargs): + rrkwargs[name.lower()] = [int(x) for x in value.split(',')] + + _handle_INTERVAL = _handle_int + _handle_COUNT = _handle_int + _handle_BYSETPOS = _handle_int_list + _handle_BYMONTH = _handle_int_list + _handle_BYMONTHDAY = _handle_int_list + _handle_BYYEARDAY = _handle_int_list + _handle_BYEASTER = _handle_int_list + _handle_BYWEEKNO = _handle_int_list + _handle_BYHOUR = _handle_int_list + _handle_BYMINUTE = _handle_int_list + _handle_BYSECOND = _handle_int_list + + def _handle_FREQ(self, rrkwargs, name, value, **kwargs): + rrkwargs["freq"] = self._freq_map[value] + + def _handle_UNTIL(self, rrkwargs, name, value, **kwargs): + global parser + if not parser: + from dateutil import parser + try: + rrkwargs["until"] = parser.parse(value, + ignoretz=kwargs.get("ignoretz"), + tzinfos=kwargs.get("tzinfos")) + except ValueError: + raise ValueError("invalid until date") + + def _handle_WKST(self, rrkwargs, name, value, **kwargs): + rrkwargs["wkst"] = self._weekday_map[value] + + def _handle_BYWEEKDAY(self, rrkwargs, name, value, **kwargs): + """ + Two ways to specify this: +1MO or MO(+1) + """ + l = [] + for wday in value.split(','): + if '(' in wday: + # If it's of the form TH(+1), etc. + splt = wday.split('(') + w = splt[0] + n = int(splt[1][:-1]) + elif len(wday): + # If it's of the form +1MO + for i in range(len(wday)): + if wday[i] not in '+-0123456789': + break + n = wday[:i] or None + w = wday[i:] + if n: + n = int(n) + else: + raise ValueError("Invalid (empty) BYDAY specification.") + + l.append(weekdays[self._weekday_map[w]](n)) + rrkwargs["byweekday"] = l + + _handle_BYDAY = _handle_BYWEEKDAY + + def _parse_rfc_rrule(self, line, + dtstart=None, + cache=False, + ignoretz=False, + tzinfos=None): + if line.find(':') != -1: + name, value = line.split(':') + if name != "RRULE": + raise ValueError("unknown parameter name") + else: + value = line + rrkwargs = {} + for pair in value.split(';'): + name, value = pair.split('=') + name = name.upper() + value = value.upper() + try: + getattr(self, "_handle_"+name)(rrkwargs, name, value, + ignoretz=ignoretz, + tzinfos=tzinfos) + except AttributeError: + raise ValueError("unknown parameter '%s'" % name) + except (KeyError, ValueError): + raise ValueError("invalid '%s': %s" % (name, value)) + return rrule(dtstart=dtstart, cache=cache, **rrkwargs) + + def _parse_date_value(self, date_value, parms, rule_tzids, + ignoretz, tzids, tzinfos): + global parser + if not parser: + from dateutil import parser + + datevals = [] + value_found = False + TZID = None + + for parm in parms: + if parm.startswith("TZID="): + try: + tzkey = rule_tzids[parm.split('TZID=')[-1]] + except KeyError: + continue + if tzids is None: + from . import tz + tzlookup = tz.gettz + elif callable(tzids): + tzlookup = tzids + else: + tzlookup = getattr(tzids, 'get', None) + if tzlookup is None: + msg = ('tzids must be a callable, mapping, or None, ' + 'not %s' % tzids) + raise ValueError(msg) + + TZID = tzlookup(tzkey) + continue + + # RFC 5445 3.8.2.4: The VALUE parameter is optional, but may be found + # only once. + if parm not in {"VALUE=DATE-TIME", "VALUE=DATE"}: + raise ValueError("unsupported parm: " + parm) + else: + if value_found: + msg = ("Duplicate value parameter found in: " + parm) + raise ValueError(msg) + value_found = True + + for datestr in date_value.split(','): + date = parser.parse(datestr, ignoretz=ignoretz, tzinfos=tzinfos) + if TZID is not None: + if date.tzinfo is None: + date = date.replace(tzinfo=TZID) + else: + raise ValueError('DTSTART/EXDATE specifies multiple timezone') + datevals.append(date) + + return datevals + + def _parse_rfc(self, s, + dtstart=None, + cache=False, + unfold=False, + forceset=False, + compatible=False, + ignoretz=False, + tzids=None, + tzinfos=None): + global parser + if compatible: + forceset = True + unfold = True + + TZID_NAMES = dict(map( + lambda x: (x.upper(), x), + re.findall('TZID=(?P[^:]+):', s) + )) + s = s.upper() + if not s.strip(): + raise ValueError("empty string") + if unfold: + lines = s.splitlines() + i = 0 + while i < len(lines): + line = lines[i].rstrip() + if not line: + del lines[i] + elif i > 0 and line[0] == " ": + lines[i-1] += line[1:] + del lines[i] + else: + i += 1 + else: + lines = s.split() + if (not forceset and len(lines) == 1 and (s.find(':') == -1 or + s.startswith('RRULE:'))): + return self._parse_rfc_rrule(lines[0], cache=cache, + dtstart=dtstart, ignoretz=ignoretz, + tzinfos=tzinfos) + else: + rrulevals = [] + rdatevals = [] + exrulevals = [] + exdatevals = [] + for line in lines: + if not line: + continue + if line.find(':') == -1: + name = "RRULE" + value = line + else: + name, value = line.split(':', 1) + parms = name.split(';') + if not parms: + raise ValueError("empty property name") + name = parms[0] + parms = parms[1:] + if name == "RRULE": + for parm in parms: + raise ValueError("unsupported RRULE parm: "+parm) + rrulevals.append(value) + elif name == "RDATE": + for parm in parms: + if parm != "VALUE=DATE-TIME": + raise ValueError("unsupported RDATE parm: "+parm) + rdatevals.append(value) + elif name == "EXRULE": + for parm in parms: + raise ValueError("unsupported EXRULE parm: "+parm) + exrulevals.append(value) + elif name == "EXDATE": + exdatevals.extend( + self._parse_date_value(value, parms, + TZID_NAMES, ignoretz, + tzids, tzinfos) + ) + elif name == "DTSTART": + dtvals = self._parse_date_value(value, parms, TZID_NAMES, + ignoretz, tzids, tzinfos) + if len(dtvals) != 1: + raise ValueError("Multiple DTSTART values specified:" + + value) + dtstart = dtvals[0] + else: + raise ValueError("unsupported property: "+name) + if (forceset or len(rrulevals) > 1 or rdatevals + or exrulevals or exdatevals): + if not parser and (rdatevals or exdatevals): + from dateutil import parser + rset = rruleset(cache=cache) + for value in rrulevals: + rset.rrule(self._parse_rfc_rrule(value, dtstart=dtstart, + ignoretz=ignoretz, + tzinfos=tzinfos)) + for value in rdatevals: + for datestr in value.split(','): + rset.rdate(parser.parse(datestr, + ignoretz=ignoretz, + tzinfos=tzinfos)) + for value in exrulevals: + rset.exrule(self._parse_rfc_rrule(value, dtstart=dtstart, + ignoretz=ignoretz, + tzinfos=tzinfos)) + for value in exdatevals: + rset.exdate(value) + if compatible and dtstart: + rset.rdate(dtstart) + return rset + else: + return self._parse_rfc_rrule(rrulevals[0], + dtstart=dtstart, + cache=cache, + ignoretz=ignoretz, + tzinfos=tzinfos) + + def __call__(self, s, **kwargs): + return self._parse_rfc(s, **kwargs) + + +rrulestr = _rrulestr() + +# vim:ts=4:sw=4:et diff --git a/venv/lib/python3.12/site-packages/dateutil/tz/__init__.py b/venv/lib/python3.12/site-packages/dateutil/tz/__init__.py new file mode 100644 index 00000000..af1352c4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/dateutil/tz/__init__.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +from .tz import * +from .tz import __doc__ + +__all__ = ["tzutc", "tzoffset", "tzlocal", "tzfile", "tzrange", + "tzstr", "tzical", "tzwin", "tzwinlocal", "gettz", + "enfold", "datetime_ambiguous", "datetime_exists", + "resolve_imaginary", "UTC", "DeprecatedTzFormatWarning"] + + +class DeprecatedTzFormatWarning(Warning): + """Warning raised when time zones are parsed from deprecated formats.""" diff --git a/venv/lib/python3.12/site-packages/dateutil/tz/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/dateutil/tz/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 00000000..b064f685 Binary files /dev/null and b/venv/lib/python3.12/site-packages/dateutil/tz/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/dateutil/tz/__pycache__/_common.cpython-312.pyc b/venv/lib/python3.12/site-packages/dateutil/tz/__pycache__/_common.cpython-312.pyc new file mode 100644 index 00000000..974662c7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/dateutil/tz/__pycache__/_common.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/dateutil/tz/__pycache__/_factories.cpython-312.pyc b/venv/lib/python3.12/site-packages/dateutil/tz/__pycache__/_factories.cpython-312.pyc new file mode 100644 index 00000000..feec96b1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/dateutil/tz/__pycache__/_factories.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/dateutil/tz/__pycache__/tz.cpython-312.pyc b/venv/lib/python3.12/site-packages/dateutil/tz/__pycache__/tz.cpython-312.pyc new file mode 100644 index 00000000..babdb8fc Binary files /dev/null and b/venv/lib/python3.12/site-packages/dateutil/tz/__pycache__/tz.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/dateutil/tz/__pycache__/win.cpython-312.pyc b/venv/lib/python3.12/site-packages/dateutil/tz/__pycache__/win.cpython-312.pyc new file mode 100644 index 00000000..c20fe1b4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/dateutil/tz/__pycache__/win.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/dateutil/tz/_common.py b/venv/lib/python3.12/site-packages/dateutil/tz/_common.py new file mode 100644 index 00000000..e6ac1183 --- /dev/null +++ b/venv/lib/python3.12/site-packages/dateutil/tz/_common.py @@ -0,0 +1,419 @@ +from six import PY2 + +from functools import wraps + +from datetime import datetime, timedelta, tzinfo + + +ZERO = timedelta(0) + +__all__ = ['tzname_in_python2', 'enfold'] + + +def tzname_in_python2(namefunc): + """Change unicode output into bytestrings in Python 2 + + tzname() API changed in Python 3. It used to return bytes, but was changed + to unicode strings + """ + if PY2: + @wraps(namefunc) + def adjust_encoding(*args, **kwargs): + name = namefunc(*args, **kwargs) + if name is not None: + name = name.encode() + + return name + + return adjust_encoding + else: + return namefunc + + +# The following is adapted from Alexander Belopolsky's tz library +# https://github.com/abalkin/tz +if hasattr(datetime, 'fold'): + # This is the pre-python 3.6 fold situation + def enfold(dt, fold=1): + """ + Provides a unified interface for assigning the ``fold`` attribute to + datetimes both before and after the implementation of PEP-495. + + :param fold: + The value for the ``fold`` attribute in the returned datetime. This + should be either 0 or 1. + + :return: + Returns an object for which ``getattr(dt, 'fold', 0)`` returns + ``fold`` for all versions of Python. In versions prior to + Python 3.6, this is a ``_DatetimeWithFold`` object, which is a + subclass of :py:class:`datetime.datetime` with the ``fold`` + attribute added, if ``fold`` is 1. + + .. versionadded:: 2.6.0 + """ + return dt.replace(fold=fold) + +else: + class _DatetimeWithFold(datetime): + """ + This is a class designed to provide a PEP 495-compliant interface for + Python versions before 3.6. It is used only for dates in a fold, so + the ``fold`` attribute is fixed at ``1``. + + .. versionadded:: 2.6.0 + """ + __slots__ = () + + def replace(self, *args, **kwargs): + """ + Return a datetime with the same attributes, except for those + attributes given new values by whichever keyword arguments are + specified. Note that tzinfo=None can be specified to create a naive + datetime from an aware datetime with no conversion of date and time + data. + + This is reimplemented in ``_DatetimeWithFold`` because pypy3 will + return a ``datetime.datetime`` even if ``fold`` is unchanged. + """ + argnames = ( + 'year', 'month', 'day', 'hour', 'minute', 'second', + 'microsecond', 'tzinfo' + ) + + for arg, argname in zip(args, argnames): + if argname in kwargs: + raise TypeError('Duplicate argument: {}'.format(argname)) + + kwargs[argname] = arg + + for argname in argnames: + if argname not in kwargs: + kwargs[argname] = getattr(self, argname) + + dt_class = self.__class__ if kwargs.get('fold', 1) else datetime + + return dt_class(**kwargs) + + @property + def fold(self): + return 1 + + def enfold(dt, fold=1): + """ + Provides a unified interface for assigning the ``fold`` attribute to + datetimes both before and after the implementation of PEP-495. + + :param fold: + The value for the ``fold`` attribute in the returned datetime. This + should be either 0 or 1. + + :return: + Returns an object for which ``getattr(dt, 'fold', 0)`` returns + ``fold`` for all versions of Python. In versions prior to + Python 3.6, this is a ``_DatetimeWithFold`` object, which is a + subclass of :py:class:`datetime.datetime` with the ``fold`` + attribute added, if ``fold`` is 1. + + .. versionadded:: 2.6.0 + """ + if getattr(dt, 'fold', 0) == fold: + return dt + + args = dt.timetuple()[:6] + args += (dt.microsecond, dt.tzinfo) + + if fold: + return _DatetimeWithFold(*args) + else: + return datetime(*args) + + +def _validate_fromutc_inputs(f): + """ + The CPython version of ``fromutc`` checks that the input is a ``datetime`` + object and that ``self`` is attached as its ``tzinfo``. + """ + @wraps(f) + def fromutc(self, dt): + if not isinstance(dt, datetime): + raise TypeError("fromutc() requires a datetime argument") + if dt.tzinfo is not self: + raise ValueError("dt.tzinfo is not self") + + return f(self, dt) + + return fromutc + + +class _tzinfo(tzinfo): + """ + Base class for all ``dateutil`` ``tzinfo`` objects. + """ + + def is_ambiguous(self, dt): + """ + Whether or not the "wall time" of a given datetime is ambiguous in this + zone. + + :param dt: + A :py:class:`datetime.datetime`, naive or time zone aware. + + + :return: + Returns ``True`` if ambiguous, ``False`` otherwise. + + .. versionadded:: 2.6.0 + """ + + dt = dt.replace(tzinfo=self) + + wall_0 = enfold(dt, fold=0) + wall_1 = enfold(dt, fold=1) + + same_offset = wall_0.utcoffset() == wall_1.utcoffset() + same_dt = wall_0.replace(tzinfo=None) == wall_1.replace(tzinfo=None) + + return same_dt and not same_offset + + def _fold_status(self, dt_utc, dt_wall): + """ + Determine the fold status of a "wall" datetime, given a representation + of the same datetime as a (naive) UTC datetime. This is calculated based + on the assumption that ``dt.utcoffset() - dt.dst()`` is constant for all + datetimes, and that this offset is the actual number of hours separating + ``dt_utc`` and ``dt_wall``. + + :param dt_utc: + Representation of the datetime as UTC + + :param dt_wall: + Representation of the datetime as "wall time". This parameter must + either have a `fold` attribute or have a fold-naive + :class:`datetime.tzinfo` attached, otherwise the calculation may + fail. + """ + if self.is_ambiguous(dt_wall): + delta_wall = dt_wall - dt_utc + _fold = int(delta_wall == (dt_utc.utcoffset() - dt_utc.dst())) + else: + _fold = 0 + + return _fold + + def _fold(self, dt): + return getattr(dt, 'fold', 0) + + def _fromutc(self, dt): + """ + Given a timezone-aware datetime in a given timezone, calculates a + timezone-aware datetime in a new timezone. + + Since this is the one time that we *know* we have an unambiguous + datetime object, we take this opportunity to determine whether the + datetime is ambiguous and in a "fold" state (e.g. if it's the first + occurrence, chronologically, of the ambiguous datetime). + + :param dt: + A timezone-aware :class:`datetime.datetime` object. + """ + + # Re-implement the algorithm from Python's datetime.py + dtoff = dt.utcoffset() + if dtoff is None: + raise ValueError("fromutc() requires a non-None utcoffset() " + "result") + + # The original datetime.py code assumes that `dst()` defaults to + # zero during ambiguous times. PEP 495 inverts this presumption, so + # for pre-PEP 495 versions of python, we need to tweak the algorithm. + dtdst = dt.dst() + if dtdst is None: + raise ValueError("fromutc() requires a non-None dst() result") + delta = dtoff - dtdst + + dt += delta + # Set fold=1 so we can default to being in the fold for + # ambiguous dates. + dtdst = enfold(dt, fold=1).dst() + if dtdst is None: + raise ValueError("fromutc(): dt.dst gave inconsistent " + "results; cannot convert") + return dt + dtdst + + @_validate_fromutc_inputs + def fromutc(self, dt): + """ + Given a timezone-aware datetime in a given timezone, calculates a + timezone-aware datetime in a new timezone. + + Since this is the one time that we *know* we have an unambiguous + datetime object, we take this opportunity to determine whether the + datetime is ambiguous and in a "fold" state (e.g. if it's the first + occurrence, chronologically, of the ambiguous datetime). + + :param dt: + A timezone-aware :class:`datetime.datetime` object. + """ + dt_wall = self._fromutc(dt) + + # Calculate the fold status given the two datetimes. + _fold = self._fold_status(dt, dt_wall) + + # Set the default fold value for ambiguous dates + return enfold(dt_wall, fold=_fold) + + +class tzrangebase(_tzinfo): + """ + This is an abstract base class for time zones represented by an annual + transition into and out of DST. Child classes should implement the following + methods: + + * ``__init__(self, *args, **kwargs)`` + * ``transitions(self, year)`` - this is expected to return a tuple of + datetimes representing the DST on and off transitions in standard + time. + + A fully initialized ``tzrangebase`` subclass should also provide the + following attributes: + * ``hasdst``: Boolean whether or not the zone uses DST. + * ``_dst_offset`` / ``_std_offset``: :class:`datetime.timedelta` objects + representing the respective UTC offsets. + * ``_dst_abbr`` / ``_std_abbr``: Strings representing the timezone short + abbreviations in DST and STD, respectively. + * ``_hasdst``: Whether or not the zone has DST. + + .. versionadded:: 2.6.0 + """ + def __init__(self): + raise NotImplementedError('tzrangebase is an abstract base class') + + def utcoffset(self, dt): + isdst = self._isdst(dt) + + if isdst is None: + return None + elif isdst: + return self._dst_offset + else: + return self._std_offset + + def dst(self, dt): + isdst = self._isdst(dt) + + if isdst is None: + return None + elif isdst: + return self._dst_base_offset + else: + return ZERO + + @tzname_in_python2 + def tzname(self, dt): + if self._isdst(dt): + return self._dst_abbr + else: + return self._std_abbr + + def fromutc(self, dt): + """ Given a datetime in UTC, return local time """ + if not isinstance(dt, datetime): + raise TypeError("fromutc() requires a datetime argument") + + if dt.tzinfo is not self: + raise ValueError("dt.tzinfo is not self") + + # Get transitions - if there are none, fixed offset + transitions = self.transitions(dt.year) + if transitions is None: + return dt + self.utcoffset(dt) + + # Get the transition times in UTC + dston, dstoff = transitions + + dston -= self._std_offset + dstoff -= self._std_offset + + utc_transitions = (dston, dstoff) + dt_utc = dt.replace(tzinfo=None) + + isdst = self._naive_isdst(dt_utc, utc_transitions) + + if isdst: + dt_wall = dt + self._dst_offset + else: + dt_wall = dt + self._std_offset + + _fold = int(not isdst and self.is_ambiguous(dt_wall)) + + return enfold(dt_wall, fold=_fold) + + def is_ambiguous(self, dt): + """ + Whether or not the "wall time" of a given datetime is ambiguous in this + zone. + + :param dt: + A :py:class:`datetime.datetime`, naive or time zone aware. + + + :return: + Returns ``True`` if ambiguous, ``False`` otherwise. + + .. versionadded:: 2.6.0 + """ + if not self.hasdst: + return False + + start, end = self.transitions(dt.year) + + dt = dt.replace(tzinfo=None) + return (end <= dt < end + self._dst_base_offset) + + def _isdst(self, dt): + if not self.hasdst: + return False + elif dt is None: + return None + + transitions = self.transitions(dt.year) + + if transitions is None: + return False + + dt = dt.replace(tzinfo=None) + + isdst = self._naive_isdst(dt, transitions) + + # Handle ambiguous dates + if not isdst and self.is_ambiguous(dt): + return not self._fold(dt) + else: + return isdst + + def _naive_isdst(self, dt, transitions): + dston, dstoff = transitions + + dt = dt.replace(tzinfo=None) + + if dston < dstoff: + isdst = dston <= dt < dstoff + else: + isdst = not dstoff <= dt < dston + + return isdst + + @property + def _dst_base_offset(self): + return self._dst_offset - self._std_offset + + __hash__ = None + + def __ne__(self, other): + return not (self == other) + + def __repr__(self): + return "%s(...)" % self.__class__.__name__ + + __reduce__ = object.__reduce__ diff --git a/venv/lib/python3.12/site-packages/dateutil/tz/_factories.py b/venv/lib/python3.12/site-packages/dateutil/tz/_factories.py new file mode 100644 index 00000000..f8a65891 --- /dev/null +++ b/venv/lib/python3.12/site-packages/dateutil/tz/_factories.py @@ -0,0 +1,80 @@ +from datetime import timedelta +import weakref +from collections import OrderedDict + +from six.moves import _thread + + +class _TzSingleton(type): + def __init__(cls, *args, **kwargs): + cls.__instance = None + super(_TzSingleton, cls).__init__(*args, **kwargs) + + def __call__(cls): + if cls.__instance is None: + cls.__instance = super(_TzSingleton, cls).__call__() + return cls.__instance + + +class _TzFactory(type): + def instance(cls, *args, **kwargs): + """Alternate constructor that returns a fresh instance""" + return type.__call__(cls, *args, **kwargs) + + +class _TzOffsetFactory(_TzFactory): + def __init__(cls, *args, **kwargs): + cls.__instances = weakref.WeakValueDictionary() + cls.__strong_cache = OrderedDict() + cls.__strong_cache_size = 8 + + cls._cache_lock = _thread.allocate_lock() + + def __call__(cls, name, offset): + if isinstance(offset, timedelta): + key = (name, offset.total_seconds()) + else: + key = (name, offset) + + instance = cls.__instances.get(key, None) + if instance is None: + instance = cls.__instances.setdefault(key, + cls.instance(name, offset)) + + # This lock may not be necessary in Python 3. See GH issue #901 + with cls._cache_lock: + cls.__strong_cache[key] = cls.__strong_cache.pop(key, instance) + + # Remove an item if the strong cache is overpopulated + if len(cls.__strong_cache) > cls.__strong_cache_size: + cls.__strong_cache.popitem(last=False) + + return instance + + +class _TzStrFactory(_TzFactory): + def __init__(cls, *args, **kwargs): + cls.__instances = weakref.WeakValueDictionary() + cls.__strong_cache = OrderedDict() + cls.__strong_cache_size = 8 + + cls.__cache_lock = _thread.allocate_lock() + + def __call__(cls, s, posix_offset=False): + key = (s, posix_offset) + instance = cls.__instances.get(key, None) + + if instance is None: + instance = cls.__instances.setdefault(key, + cls.instance(s, posix_offset)) + + # This lock may not be necessary in Python 3. See GH issue #901 + with cls.__cache_lock: + cls.__strong_cache[key] = cls.__strong_cache.pop(key, instance) + + # Remove an item if the strong cache is overpopulated + if len(cls.__strong_cache) > cls.__strong_cache_size: + cls.__strong_cache.popitem(last=False) + + return instance + diff --git a/venv/lib/python3.12/site-packages/dateutil/tz/tz.py b/venv/lib/python3.12/site-packages/dateutil/tz/tz.py new file mode 100644 index 00000000..61759144 --- /dev/null +++ b/venv/lib/python3.12/site-packages/dateutil/tz/tz.py @@ -0,0 +1,1849 @@ +# -*- coding: utf-8 -*- +""" +This module offers timezone implementations subclassing the abstract +:py:class:`datetime.tzinfo` type. There are classes to handle tzfile format +files (usually are in :file:`/etc/localtime`, :file:`/usr/share/zoneinfo`, +etc), TZ environment string (in all known formats), given ranges (with help +from relative deltas), local machine timezone, fixed offset timezone, and UTC +timezone. +""" +import datetime +import struct +import time +import sys +import os +import bisect +import weakref +from collections import OrderedDict + +import six +from six import string_types +from six.moves import _thread +from ._common import tzname_in_python2, _tzinfo +from ._common import tzrangebase, enfold +from ._common import _validate_fromutc_inputs + +from ._factories import _TzSingleton, _TzOffsetFactory +from ._factories import _TzStrFactory +try: + from .win import tzwin, tzwinlocal +except ImportError: + tzwin = tzwinlocal = None + +# For warning about rounding tzinfo +from warnings import warn + +ZERO = datetime.timedelta(0) +EPOCH = datetime.datetime(1970, 1, 1, 0, 0) +EPOCHORDINAL = EPOCH.toordinal() + + +@six.add_metaclass(_TzSingleton) +class tzutc(datetime.tzinfo): + """ + This is a tzinfo object that represents the UTC time zone. + + **Examples:** + + .. doctest:: + + >>> from datetime import * + >>> from dateutil.tz import * + + >>> datetime.now() + datetime.datetime(2003, 9, 27, 9, 40, 1, 521290) + + >>> datetime.now(tzutc()) + datetime.datetime(2003, 9, 27, 12, 40, 12, 156379, tzinfo=tzutc()) + + >>> datetime.now(tzutc()).tzname() + 'UTC' + + .. versionchanged:: 2.7.0 + ``tzutc()`` is now a singleton, so the result of ``tzutc()`` will + always return the same object. + + .. doctest:: + + >>> from dateutil.tz import tzutc, UTC + >>> tzutc() is tzutc() + True + >>> tzutc() is UTC + True + """ + def utcoffset(self, dt): + return ZERO + + def dst(self, dt): + return ZERO + + @tzname_in_python2 + def tzname(self, dt): + return "UTC" + + def is_ambiguous(self, dt): + """ + Whether or not the "wall time" of a given datetime is ambiguous in this + zone. + + :param dt: + A :py:class:`datetime.datetime`, naive or time zone aware. + + + :return: + Returns ``True`` if ambiguous, ``False`` otherwise. + + .. versionadded:: 2.6.0 + """ + return False + + @_validate_fromutc_inputs + def fromutc(self, dt): + """ + Fast track version of fromutc() returns the original ``dt`` object for + any valid :py:class:`datetime.datetime` object. + """ + return dt + + def __eq__(self, other): + if not isinstance(other, (tzutc, tzoffset)): + return NotImplemented + + return (isinstance(other, tzutc) or + (isinstance(other, tzoffset) and other._offset == ZERO)) + + __hash__ = None + + def __ne__(self, other): + return not (self == other) + + def __repr__(self): + return "%s()" % self.__class__.__name__ + + __reduce__ = object.__reduce__ + + +#: Convenience constant providing a :class:`tzutc()` instance +#: +#: .. versionadded:: 2.7.0 +UTC = tzutc() + + +@six.add_metaclass(_TzOffsetFactory) +class tzoffset(datetime.tzinfo): + """ + A simple class for representing a fixed offset from UTC. + + :param name: + The timezone name, to be returned when ``tzname()`` is called. + :param offset: + The time zone offset in seconds, or (since version 2.6.0, represented + as a :py:class:`datetime.timedelta` object). + """ + def __init__(self, name, offset): + self._name = name + + try: + # Allow a timedelta + offset = offset.total_seconds() + except (TypeError, AttributeError): + pass + + self._offset = datetime.timedelta(seconds=_get_supported_offset(offset)) + + def utcoffset(self, dt): + return self._offset + + def dst(self, dt): + return ZERO + + @tzname_in_python2 + def tzname(self, dt): + return self._name + + @_validate_fromutc_inputs + def fromutc(self, dt): + return dt + self._offset + + def is_ambiguous(self, dt): + """ + Whether or not the "wall time" of a given datetime is ambiguous in this + zone. + + :param dt: + A :py:class:`datetime.datetime`, naive or time zone aware. + :return: + Returns ``True`` if ambiguous, ``False`` otherwise. + + .. versionadded:: 2.6.0 + """ + return False + + def __eq__(self, other): + if not isinstance(other, tzoffset): + return NotImplemented + + return self._offset == other._offset + + __hash__ = None + + def __ne__(self, other): + return not (self == other) + + def __repr__(self): + return "%s(%s, %s)" % (self.__class__.__name__, + repr(self._name), + int(self._offset.total_seconds())) + + __reduce__ = object.__reduce__ + + +class tzlocal(_tzinfo): + """ + A :class:`tzinfo` subclass built around the ``time`` timezone functions. + """ + def __init__(self): + super(tzlocal, self).__init__() + + self._std_offset = datetime.timedelta(seconds=-time.timezone) + if time.daylight: + self._dst_offset = datetime.timedelta(seconds=-time.altzone) + else: + self._dst_offset = self._std_offset + + self._dst_saved = self._dst_offset - self._std_offset + self._hasdst = bool(self._dst_saved) + self._tznames = tuple(time.tzname) + + def utcoffset(self, dt): + if dt is None and self._hasdst: + return None + + if self._isdst(dt): + return self._dst_offset + else: + return self._std_offset + + def dst(self, dt): + if dt is None and self._hasdst: + return None + + if self._isdst(dt): + return self._dst_offset - self._std_offset + else: + return ZERO + + @tzname_in_python2 + def tzname(self, dt): + return self._tznames[self._isdst(dt)] + + def is_ambiguous(self, dt): + """ + Whether or not the "wall time" of a given datetime is ambiguous in this + zone. + + :param dt: + A :py:class:`datetime.datetime`, naive or time zone aware. + + + :return: + Returns ``True`` if ambiguous, ``False`` otherwise. + + .. versionadded:: 2.6.0 + """ + naive_dst = self._naive_is_dst(dt) + return (not naive_dst and + (naive_dst != self._naive_is_dst(dt - self._dst_saved))) + + def _naive_is_dst(self, dt): + timestamp = _datetime_to_timestamp(dt) + return time.localtime(timestamp + time.timezone).tm_isdst + + def _isdst(self, dt, fold_naive=True): + # We can't use mktime here. It is unstable when deciding if + # the hour near to a change is DST or not. + # + # timestamp = time.mktime((dt.year, dt.month, dt.day, dt.hour, + # dt.minute, dt.second, dt.weekday(), 0, -1)) + # return time.localtime(timestamp).tm_isdst + # + # The code above yields the following result: + # + # >>> import tz, datetime + # >>> t = tz.tzlocal() + # >>> datetime.datetime(2003,2,15,23,tzinfo=t).tzname() + # 'BRDT' + # >>> datetime.datetime(2003,2,16,0,tzinfo=t).tzname() + # 'BRST' + # >>> datetime.datetime(2003,2,15,23,tzinfo=t).tzname() + # 'BRST' + # >>> datetime.datetime(2003,2,15,22,tzinfo=t).tzname() + # 'BRDT' + # >>> datetime.datetime(2003,2,15,23,tzinfo=t).tzname() + # 'BRDT' + # + # Here is a more stable implementation: + # + if not self._hasdst: + return False + + # Check for ambiguous times: + dstval = self._naive_is_dst(dt) + fold = getattr(dt, 'fold', None) + + if self.is_ambiguous(dt): + if fold is not None: + return not self._fold(dt) + else: + return True + + return dstval + + def __eq__(self, other): + if isinstance(other, tzlocal): + return (self._std_offset == other._std_offset and + self._dst_offset == other._dst_offset) + elif isinstance(other, tzutc): + return (not self._hasdst and + self._tznames[0] in {'UTC', 'GMT'} and + self._std_offset == ZERO) + elif isinstance(other, tzoffset): + return (not self._hasdst and + self._tznames[0] == other._name and + self._std_offset == other._offset) + else: + return NotImplemented + + __hash__ = None + + def __ne__(self, other): + return not (self == other) + + def __repr__(self): + return "%s()" % self.__class__.__name__ + + __reduce__ = object.__reduce__ + + +class _ttinfo(object): + __slots__ = ["offset", "delta", "isdst", "abbr", + "isstd", "isgmt", "dstoffset"] + + def __init__(self): + for attr in self.__slots__: + setattr(self, attr, None) + + def __repr__(self): + l = [] + for attr in self.__slots__: + value = getattr(self, attr) + if value is not None: + l.append("%s=%s" % (attr, repr(value))) + return "%s(%s)" % (self.__class__.__name__, ", ".join(l)) + + def __eq__(self, other): + if not isinstance(other, _ttinfo): + return NotImplemented + + return (self.offset == other.offset and + self.delta == other.delta and + self.isdst == other.isdst and + self.abbr == other.abbr and + self.isstd == other.isstd and + self.isgmt == other.isgmt and + self.dstoffset == other.dstoffset) + + __hash__ = None + + def __ne__(self, other): + return not (self == other) + + def __getstate__(self): + state = {} + for name in self.__slots__: + state[name] = getattr(self, name, None) + return state + + def __setstate__(self, state): + for name in self.__slots__: + if name in state: + setattr(self, name, state[name]) + + +class _tzfile(object): + """ + Lightweight class for holding the relevant transition and time zone + information read from binary tzfiles. + """ + attrs = ['trans_list', 'trans_list_utc', 'trans_idx', 'ttinfo_list', + 'ttinfo_std', 'ttinfo_dst', 'ttinfo_before', 'ttinfo_first'] + + def __init__(self, **kwargs): + for attr in self.attrs: + setattr(self, attr, kwargs.get(attr, None)) + + +class tzfile(_tzinfo): + """ + This is a ``tzinfo`` subclass that allows one to use the ``tzfile(5)`` + format timezone files to extract current and historical zone information. + + :param fileobj: + This can be an opened file stream or a file name that the time zone + information can be read from. + + :param filename: + This is an optional parameter specifying the source of the time zone + information in the event that ``fileobj`` is a file object. If omitted + and ``fileobj`` is a file stream, this parameter will be set either to + ``fileobj``'s ``name`` attribute or to ``repr(fileobj)``. + + See `Sources for Time Zone and Daylight Saving Time Data + `_ for more information. + Time zone files can be compiled from the `IANA Time Zone database files + `_ with the `zic time zone compiler + `_ + + .. note:: + + Only construct a ``tzfile`` directly if you have a specific timezone + file on disk that you want to read into a Python ``tzinfo`` object. + If you want to get a ``tzfile`` representing a specific IANA zone, + (e.g. ``'America/New_York'``), you should call + :func:`dateutil.tz.gettz` with the zone identifier. + + + **Examples:** + + Using the US Eastern time zone as an example, we can see that a ``tzfile`` + provides time zone information for the standard Daylight Saving offsets: + + .. testsetup:: tzfile + + from dateutil.tz import gettz + from datetime import datetime + + .. doctest:: tzfile + + >>> NYC = gettz('America/New_York') + >>> NYC + tzfile('/usr/share/zoneinfo/America/New_York') + + >>> print(datetime(2016, 1, 3, tzinfo=NYC)) # EST + 2016-01-03 00:00:00-05:00 + + >>> print(datetime(2016, 7, 7, tzinfo=NYC)) # EDT + 2016-07-07 00:00:00-04:00 + + + The ``tzfile`` structure contains a fully history of the time zone, + so historical dates will also have the right offsets. For example, before + the adoption of the UTC standards, New York used local solar mean time: + + .. doctest:: tzfile + + >>> print(datetime(1901, 4, 12, tzinfo=NYC)) # LMT + 1901-04-12 00:00:00-04:56 + + And during World War II, New York was on "Eastern War Time", which was a + state of permanent daylight saving time: + + .. doctest:: tzfile + + >>> print(datetime(1944, 2, 7, tzinfo=NYC)) # EWT + 1944-02-07 00:00:00-04:00 + + """ + + def __init__(self, fileobj, filename=None): + super(tzfile, self).__init__() + + file_opened_here = False + if isinstance(fileobj, string_types): + self._filename = fileobj + fileobj = open(fileobj, 'rb') + file_opened_here = True + elif filename is not None: + self._filename = filename + elif hasattr(fileobj, "name"): + self._filename = fileobj.name + else: + self._filename = repr(fileobj) + + if fileobj is not None: + if not file_opened_here: + fileobj = _nullcontext(fileobj) + + with fileobj as file_stream: + tzobj = self._read_tzfile(file_stream) + + self._set_tzdata(tzobj) + + def _set_tzdata(self, tzobj): + """ Set the time zone data of this object from a _tzfile object """ + # Copy the relevant attributes over as private attributes + for attr in _tzfile.attrs: + setattr(self, '_' + attr, getattr(tzobj, attr)) + + def _read_tzfile(self, fileobj): + out = _tzfile() + + # From tzfile(5): + # + # The time zone information files used by tzset(3) + # begin with the magic characters "TZif" to identify + # them as time zone information files, followed by + # sixteen bytes reserved for future use, followed by + # six four-byte values of type long, written in a + # ``standard'' byte order (the high-order byte + # of the value is written first). + if fileobj.read(4).decode() != "TZif": + raise ValueError("magic not found") + + fileobj.read(16) + + ( + # The number of UTC/local indicators stored in the file. + ttisgmtcnt, + + # The number of standard/wall indicators stored in the file. + ttisstdcnt, + + # The number of leap seconds for which data is + # stored in the file. + leapcnt, + + # The number of "transition times" for which data + # is stored in the file. + timecnt, + + # The number of "local time types" for which data + # is stored in the file (must not be zero). + typecnt, + + # The number of characters of "time zone + # abbreviation strings" stored in the file. + charcnt, + + ) = struct.unpack(">6l", fileobj.read(24)) + + # The above header is followed by tzh_timecnt four-byte + # values of type long, sorted in ascending order. + # These values are written in ``standard'' byte order. + # Each is used as a transition time (as returned by + # time(2)) at which the rules for computing local time + # change. + + if timecnt: + out.trans_list_utc = list(struct.unpack(">%dl" % timecnt, + fileobj.read(timecnt*4))) + else: + out.trans_list_utc = [] + + # Next come tzh_timecnt one-byte values of type unsigned + # char; each one tells which of the different types of + # ``local time'' types described in the file is associated + # with the same-indexed transition time. These values + # serve as indices into an array of ttinfo structures that + # appears next in the file. + + if timecnt: + out.trans_idx = struct.unpack(">%dB" % timecnt, + fileobj.read(timecnt)) + else: + out.trans_idx = [] + + # Each ttinfo structure is written as a four-byte value + # for tt_gmtoff of type long, in a standard byte + # order, followed by a one-byte value for tt_isdst + # and a one-byte value for tt_abbrind. In each + # structure, tt_gmtoff gives the number of + # seconds to be added to UTC, tt_isdst tells whether + # tm_isdst should be set by localtime(3), and + # tt_abbrind serves as an index into the array of + # time zone abbreviation characters that follow the + # ttinfo structure(s) in the file. + + ttinfo = [] + + for i in range(typecnt): + ttinfo.append(struct.unpack(">lbb", fileobj.read(6))) + + abbr = fileobj.read(charcnt).decode() + + # Then there are tzh_leapcnt pairs of four-byte + # values, written in standard byte order; the + # first value of each pair gives the time (as + # returned by time(2)) at which a leap second + # occurs; the second gives the total number of + # leap seconds to be applied after the given time. + # The pairs of values are sorted in ascending order + # by time. + + # Not used, for now (but seek for correct file position) + if leapcnt: + fileobj.seek(leapcnt * 8, os.SEEK_CUR) + + # Then there are tzh_ttisstdcnt standard/wall + # indicators, each stored as a one-byte value; + # they tell whether the transition times associated + # with local time types were specified as standard + # time or wall clock time, and are used when + # a time zone file is used in handling POSIX-style + # time zone environment variables. + + if ttisstdcnt: + isstd = struct.unpack(">%db" % ttisstdcnt, + fileobj.read(ttisstdcnt)) + + # Finally, there are tzh_ttisgmtcnt UTC/local + # indicators, each stored as a one-byte value; + # they tell whether the transition times associated + # with local time types were specified as UTC or + # local time, and are used when a time zone file + # is used in handling POSIX-style time zone envi- + # ronment variables. + + if ttisgmtcnt: + isgmt = struct.unpack(">%db" % ttisgmtcnt, + fileobj.read(ttisgmtcnt)) + + # Build ttinfo list + out.ttinfo_list = [] + for i in range(typecnt): + gmtoff, isdst, abbrind = ttinfo[i] + gmtoff = _get_supported_offset(gmtoff) + tti = _ttinfo() + tti.offset = gmtoff + tti.dstoffset = datetime.timedelta(0) + tti.delta = datetime.timedelta(seconds=gmtoff) + tti.isdst = isdst + tti.abbr = abbr[abbrind:abbr.find('\x00', abbrind)] + tti.isstd = (ttisstdcnt > i and isstd[i] != 0) + tti.isgmt = (ttisgmtcnt > i and isgmt[i] != 0) + out.ttinfo_list.append(tti) + + # Replace ttinfo indexes for ttinfo objects. + out.trans_idx = [out.ttinfo_list[idx] for idx in out.trans_idx] + + # Set standard, dst, and before ttinfos. before will be + # used when a given time is before any transitions, + # and will be set to the first non-dst ttinfo, or to + # the first dst, if all of them are dst. + out.ttinfo_std = None + out.ttinfo_dst = None + out.ttinfo_before = None + if out.ttinfo_list: + if not out.trans_list_utc: + out.ttinfo_std = out.ttinfo_first = out.ttinfo_list[0] + else: + for i in range(timecnt-1, -1, -1): + tti = out.trans_idx[i] + if not out.ttinfo_std and not tti.isdst: + out.ttinfo_std = tti + elif not out.ttinfo_dst and tti.isdst: + out.ttinfo_dst = tti + + if out.ttinfo_std and out.ttinfo_dst: + break + else: + if out.ttinfo_dst and not out.ttinfo_std: + out.ttinfo_std = out.ttinfo_dst + + for tti in out.ttinfo_list: + if not tti.isdst: + out.ttinfo_before = tti + break + else: + out.ttinfo_before = out.ttinfo_list[0] + + # Now fix transition times to become relative to wall time. + # + # I'm not sure about this. In my tests, the tz source file + # is setup to wall time, and in the binary file isstd and + # isgmt are off, so it should be in wall time. OTOH, it's + # always in gmt time. Let me know if you have comments + # about this. + lastdst = None + lastoffset = None + lastdstoffset = None + lastbaseoffset = None + out.trans_list = [] + + for i, tti in enumerate(out.trans_idx): + offset = tti.offset + dstoffset = 0 + + if lastdst is not None: + if tti.isdst: + if not lastdst: + dstoffset = offset - lastoffset + + if not dstoffset and lastdstoffset: + dstoffset = lastdstoffset + + tti.dstoffset = datetime.timedelta(seconds=dstoffset) + lastdstoffset = dstoffset + + # If a time zone changes its base offset during a DST transition, + # then you need to adjust by the previous base offset to get the + # transition time in local time. Otherwise you use the current + # base offset. Ideally, I would have some mathematical proof of + # why this is true, but I haven't really thought about it enough. + baseoffset = offset - dstoffset + adjustment = baseoffset + if (lastbaseoffset is not None and baseoffset != lastbaseoffset + and tti.isdst != lastdst): + # The base DST has changed + adjustment = lastbaseoffset + + lastdst = tti.isdst + lastoffset = offset + lastbaseoffset = baseoffset + + out.trans_list.append(out.trans_list_utc[i] + adjustment) + + out.trans_idx = tuple(out.trans_idx) + out.trans_list = tuple(out.trans_list) + out.trans_list_utc = tuple(out.trans_list_utc) + + return out + + def _find_last_transition(self, dt, in_utc=False): + # If there's no list, there are no transitions to find + if not self._trans_list: + return None + + timestamp = _datetime_to_timestamp(dt) + + # Find where the timestamp fits in the transition list - if the + # timestamp is a transition time, it's part of the "after" period. + trans_list = self._trans_list_utc if in_utc else self._trans_list + idx = bisect.bisect_right(trans_list, timestamp) + + # We want to know when the previous transition was, so subtract off 1 + return idx - 1 + + def _get_ttinfo(self, idx): + # For no list or after the last transition, default to _ttinfo_std + if idx is None or (idx + 1) >= len(self._trans_list): + return self._ttinfo_std + + # If there is a list and the time is before it, return _ttinfo_before + if idx < 0: + return self._ttinfo_before + + return self._trans_idx[idx] + + def _find_ttinfo(self, dt): + idx = self._resolve_ambiguous_time(dt) + + return self._get_ttinfo(idx) + + def fromutc(self, dt): + """ + The ``tzfile`` implementation of :py:func:`datetime.tzinfo.fromutc`. + + :param dt: + A :py:class:`datetime.datetime` object. + + :raises TypeError: + Raised if ``dt`` is not a :py:class:`datetime.datetime` object. + + :raises ValueError: + Raised if this is called with a ``dt`` which does not have this + ``tzinfo`` attached. + + :return: + Returns a :py:class:`datetime.datetime` object representing the + wall time in ``self``'s time zone. + """ + # These isinstance checks are in datetime.tzinfo, so we'll preserve + # them, even if we don't care about duck typing. + if not isinstance(dt, datetime.datetime): + raise TypeError("fromutc() requires a datetime argument") + + if dt.tzinfo is not self: + raise ValueError("dt.tzinfo is not self") + + # First treat UTC as wall time and get the transition we're in. + idx = self._find_last_transition(dt, in_utc=True) + tti = self._get_ttinfo(idx) + + dt_out = dt + datetime.timedelta(seconds=tti.offset) + + fold = self.is_ambiguous(dt_out, idx=idx) + + return enfold(dt_out, fold=int(fold)) + + def is_ambiguous(self, dt, idx=None): + """ + Whether or not the "wall time" of a given datetime is ambiguous in this + zone. + + :param dt: + A :py:class:`datetime.datetime`, naive or time zone aware. + + + :return: + Returns ``True`` if ambiguous, ``False`` otherwise. + + .. versionadded:: 2.6.0 + """ + if idx is None: + idx = self._find_last_transition(dt) + + # Calculate the difference in offsets from current to previous + timestamp = _datetime_to_timestamp(dt) + tti = self._get_ttinfo(idx) + + if idx is None or idx <= 0: + return False + + od = self._get_ttinfo(idx - 1).offset - tti.offset + tt = self._trans_list[idx] # Transition time + + return timestamp < tt + od + + def _resolve_ambiguous_time(self, dt): + idx = self._find_last_transition(dt) + + # If we have no transitions, return the index + _fold = self._fold(dt) + if idx is None or idx == 0: + return idx + + # If it's ambiguous and we're in a fold, shift to a different index. + idx_offset = int(not _fold and self.is_ambiguous(dt, idx)) + + return idx - idx_offset + + def utcoffset(self, dt): + if dt is None: + return None + + if not self._ttinfo_std: + return ZERO + + return self._find_ttinfo(dt).delta + + def dst(self, dt): + if dt is None: + return None + + if not self._ttinfo_dst: + return ZERO + + tti = self._find_ttinfo(dt) + + if not tti.isdst: + return ZERO + + # The documentation says that utcoffset()-dst() must + # be constant for every dt. + return tti.dstoffset + + @tzname_in_python2 + def tzname(self, dt): + if not self._ttinfo_std or dt is None: + return None + return self._find_ttinfo(dt).abbr + + def __eq__(self, other): + if not isinstance(other, tzfile): + return NotImplemented + return (self._trans_list == other._trans_list and + self._trans_idx == other._trans_idx and + self._ttinfo_list == other._ttinfo_list) + + __hash__ = None + + def __ne__(self, other): + return not (self == other) + + def __repr__(self): + return "%s(%s)" % (self.__class__.__name__, repr(self._filename)) + + def __reduce__(self): + return self.__reduce_ex__(None) + + def __reduce_ex__(self, protocol): + return (self.__class__, (None, self._filename), self.__dict__) + + +class tzrange(tzrangebase): + """ + The ``tzrange`` object is a time zone specified by a set of offsets and + abbreviations, equivalent to the way the ``TZ`` variable can be specified + in POSIX-like systems, but using Python delta objects to specify DST + start, end and offsets. + + :param stdabbr: + The abbreviation for standard time (e.g. ``'EST'``). + + :param stdoffset: + An integer or :class:`datetime.timedelta` object or equivalent + specifying the base offset from UTC. + + If unspecified, +00:00 is used. + + :param dstabbr: + The abbreviation for DST / "Summer" time (e.g. ``'EDT'``). + + If specified, with no other DST information, DST is assumed to occur + and the default behavior or ``dstoffset``, ``start`` and ``end`` is + used. If unspecified and no other DST information is specified, it + is assumed that this zone has no DST. + + If this is unspecified and other DST information is *is* specified, + DST occurs in the zone but the time zone abbreviation is left + unchanged. + + :param dstoffset: + A an integer or :class:`datetime.timedelta` object or equivalent + specifying the UTC offset during DST. If unspecified and any other DST + information is specified, it is assumed to be the STD offset +1 hour. + + :param start: + A :class:`relativedelta.relativedelta` object or equivalent specifying + the time and time of year that daylight savings time starts. To + specify, for example, that DST starts at 2AM on the 2nd Sunday in + March, pass: + + ``relativedelta(hours=2, month=3, day=1, weekday=SU(+2))`` + + If unspecified and any other DST information is specified, the default + value is 2 AM on the first Sunday in April. + + :param end: + A :class:`relativedelta.relativedelta` object or equivalent + representing the time and time of year that daylight savings time + ends, with the same specification method as in ``start``. One note is + that this should point to the first time in the *standard* zone, so if + a transition occurs at 2AM in the DST zone and the clocks are set back + 1 hour to 1AM, set the ``hours`` parameter to +1. + + + **Examples:** + + .. testsetup:: tzrange + + from dateutil.tz import tzrange, tzstr + + .. doctest:: tzrange + + >>> tzstr('EST5EDT') == tzrange("EST", -18000, "EDT") + True + + >>> from dateutil.relativedelta import * + >>> range1 = tzrange("EST", -18000, "EDT") + >>> range2 = tzrange("EST", -18000, "EDT", -14400, + ... relativedelta(hours=+2, month=4, day=1, + ... weekday=SU(+1)), + ... relativedelta(hours=+1, month=10, day=31, + ... weekday=SU(-1))) + >>> tzstr('EST5EDT') == range1 == range2 + True + + """ + def __init__(self, stdabbr, stdoffset=None, + dstabbr=None, dstoffset=None, + start=None, end=None): + + global relativedelta + from dateutil import relativedelta + + self._std_abbr = stdabbr + self._dst_abbr = dstabbr + + try: + stdoffset = stdoffset.total_seconds() + except (TypeError, AttributeError): + pass + + try: + dstoffset = dstoffset.total_seconds() + except (TypeError, AttributeError): + pass + + if stdoffset is not None: + self._std_offset = datetime.timedelta(seconds=stdoffset) + else: + self._std_offset = ZERO + + if dstoffset is not None: + self._dst_offset = datetime.timedelta(seconds=dstoffset) + elif dstabbr and stdoffset is not None: + self._dst_offset = self._std_offset + datetime.timedelta(hours=+1) + else: + self._dst_offset = ZERO + + if dstabbr and start is None: + self._start_delta = relativedelta.relativedelta( + hours=+2, month=4, day=1, weekday=relativedelta.SU(+1)) + else: + self._start_delta = start + + if dstabbr and end is None: + self._end_delta = relativedelta.relativedelta( + hours=+1, month=10, day=31, weekday=relativedelta.SU(-1)) + else: + self._end_delta = end + + self._dst_base_offset_ = self._dst_offset - self._std_offset + self.hasdst = bool(self._start_delta) + + def transitions(self, year): + """ + For a given year, get the DST on and off transition times, expressed + always on the standard time side. For zones with no transitions, this + function returns ``None``. + + :param year: + The year whose transitions you would like to query. + + :return: + Returns a :class:`tuple` of :class:`datetime.datetime` objects, + ``(dston, dstoff)`` for zones with an annual DST transition, or + ``None`` for fixed offset zones. + """ + if not self.hasdst: + return None + + base_year = datetime.datetime(year, 1, 1) + + start = base_year + self._start_delta + end = base_year + self._end_delta + + return (start, end) + + def __eq__(self, other): + if not isinstance(other, tzrange): + return NotImplemented + + return (self._std_abbr == other._std_abbr and + self._dst_abbr == other._dst_abbr and + self._std_offset == other._std_offset and + self._dst_offset == other._dst_offset and + self._start_delta == other._start_delta and + self._end_delta == other._end_delta) + + @property + def _dst_base_offset(self): + return self._dst_base_offset_ + + +@six.add_metaclass(_TzStrFactory) +class tzstr(tzrange): + """ + ``tzstr`` objects are time zone objects specified by a time-zone string as + it would be passed to a ``TZ`` variable on POSIX-style systems (see + the `GNU C Library: TZ Variable`_ for more details). + + There is one notable exception, which is that POSIX-style time zones use an + inverted offset format, so normally ``GMT+3`` would be parsed as an offset + 3 hours *behind* GMT. The ``tzstr`` time zone object will parse this as an + offset 3 hours *ahead* of GMT. If you would like to maintain the POSIX + behavior, pass a ``True`` value to ``posix_offset``. + + The :class:`tzrange` object provides the same functionality, but is + specified using :class:`relativedelta.relativedelta` objects. rather than + strings. + + :param s: + A time zone string in ``TZ`` variable format. This can be a + :class:`bytes` (2.x: :class:`str`), :class:`str` (2.x: + :class:`unicode`) or a stream emitting unicode characters + (e.g. :class:`StringIO`). + + :param posix_offset: + Optional. If set to ``True``, interpret strings such as ``GMT+3`` or + ``UTC+3`` as being 3 hours *behind* UTC rather than ahead, per the + POSIX standard. + + .. caution:: + + Prior to version 2.7.0, this function also supported time zones + in the format: + + * ``EST5EDT,4,0,6,7200,10,0,26,7200,3600`` + * ``EST5EDT,4,1,0,7200,10,-1,0,7200,3600`` + + This format is non-standard and has been deprecated; this function + will raise a :class:`DeprecatedTZFormatWarning` until + support is removed in a future version. + + .. _`GNU C Library: TZ Variable`: + https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html + """ + def __init__(self, s, posix_offset=False): + global parser + from dateutil.parser import _parser as parser + + self._s = s + + res = parser._parsetz(s) + if res is None or res.any_unused_tokens: + raise ValueError("unknown string format") + + # Here we break the compatibility with the TZ variable handling. + # GMT-3 actually *means* the timezone -3. + if res.stdabbr in ("GMT", "UTC") and not posix_offset: + res.stdoffset *= -1 + + # We must initialize it first, since _delta() needs + # _std_offset and _dst_offset set. Use False in start/end + # to avoid building it two times. + tzrange.__init__(self, res.stdabbr, res.stdoffset, + res.dstabbr, res.dstoffset, + start=False, end=False) + + if not res.dstabbr: + self._start_delta = None + self._end_delta = None + else: + self._start_delta = self._delta(res.start) + if self._start_delta: + self._end_delta = self._delta(res.end, isend=1) + + self.hasdst = bool(self._start_delta) + + def _delta(self, x, isend=0): + from dateutil import relativedelta + kwargs = {} + if x.month is not None: + kwargs["month"] = x.month + if x.weekday is not None: + kwargs["weekday"] = relativedelta.weekday(x.weekday, x.week) + if x.week > 0: + kwargs["day"] = 1 + else: + kwargs["day"] = 31 + elif x.day: + kwargs["day"] = x.day + elif x.yday is not None: + kwargs["yearday"] = x.yday + elif x.jyday is not None: + kwargs["nlyearday"] = x.jyday + if not kwargs: + # Default is to start on first sunday of april, and end + # on last sunday of october. + if not isend: + kwargs["month"] = 4 + kwargs["day"] = 1 + kwargs["weekday"] = relativedelta.SU(+1) + else: + kwargs["month"] = 10 + kwargs["day"] = 31 + kwargs["weekday"] = relativedelta.SU(-1) + if x.time is not None: + kwargs["seconds"] = x.time + else: + # Default is 2AM. + kwargs["seconds"] = 7200 + if isend: + # Convert to standard time, to follow the documented way + # of working with the extra hour. See the documentation + # of the tzinfo class. + delta = self._dst_offset - self._std_offset + kwargs["seconds"] -= delta.seconds + delta.days * 86400 + return relativedelta.relativedelta(**kwargs) + + def __repr__(self): + return "%s(%s)" % (self.__class__.__name__, repr(self._s)) + + +class _tzicalvtzcomp(object): + def __init__(self, tzoffsetfrom, tzoffsetto, isdst, + tzname=None, rrule=None): + self.tzoffsetfrom = datetime.timedelta(seconds=tzoffsetfrom) + self.tzoffsetto = datetime.timedelta(seconds=tzoffsetto) + self.tzoffsetdiff = self.tzoffsetto - self.tzoffsetfrom + self.isdst = isdst + self.tzname = tzname + self.rrule = rrule + + +class _tzicalvtz(_tzinfo): + def __init__(self, tzid, comps=[]): + super(_tzicalvtz, self).__init__() + + self._tzid = tzid + self._comps = comps + self._cachedate = [] + self._cachecomp = [] + self._cache_lock = _thread.allocate_lock() + + def _find_comp(self, dt): + if len(self._comps) == 1: + return self._comps[0] + + dt = dt.replace(tzinfo=None) + + try: + with self._cache_lock: + return self._cachecomp[self._cachedate.index( + (dt, self._fold(dt)))] + except ValueError: + pass + + lastcompdt = None + lastcomp = None + + for comp in self._comps: + compdt = self._find_compdt(comp, dt) + + if compdt and (not lastcompdt or lastcompdt < compdt): + lastcompdt = compdt + lastcomp = comp + + if not lastcomp: + # RFC says nothing about what to do when a given + # time is before the first onset date. We'll look for the + # first standard component, or the first component, if + # none is found. + for comp in self._comps: + if not comp.isdst: + lastcomp = comp + break + else: + lastcomp = comp[0] + + with self._cache_lock: + self._cachedate.insert(0, (dt, self._fold(dt))) + self._cachecomp.insert(0, lastcomp) + + if len(self._cachedate) > 10: + self._cachedate.pop() + self._cachecomp.pop() + + return lastcomp + + def _find_compdt(self, comp, dt): + if comp.tzoffsetdiff < ZERO and self._fold(dt): + dt -= comp.tzoffsetdiff + + compdt = comp.rrule.before(dt, inc=True) + + return compdt + + def utcoffset(self, dt): + if dt is None: + return None + + return self._find_comp(dt).tzoffsetto + + def dst(self, dt): + comp = self._find_comp(dt) + if comp.isdst: + return comp.tzoffsetdiff + else: + return ZERO + + @tzname_in_python2 + def tzname(self, dt): + return self._find_comp(dt).tzname + + def __repr__(self): + return "" % repr(self._tzid) + + __reduce__ = object.__reduce__ + + +class tzical(object): + """ + This object is designed to parse an iCalendar-style ``VTIMEZONE`` structure + as set out in `RFC 5545`_ Section 4.6.5 into one or more `tzinfo` objects. + + :param `fileobj`: + A file or stream in iCalendar format, which should be UTF-8 encoded + with CRLF endings. + + .. _`RFC 5545`: https://tools.ietf.org/html/rfc5545 + """ + def __init__(self, fileobj): + global rrule + from dateutil import rrule + + if isinstance(fileobj, string_types): + self._s = fileobj + # ical should be encoded in UTF-8 with CRLF + fileobj = open(fileobj, 'r') + else: + self._s = getattr(fileobj, 'name', repr(fileobj)) + fileobj = _nullcontext(fileobj) + + self._vtz = {} + + with fileobj as fobj: + self._parse_rfc(fobj.read()) + + def keys(self): + """ + Retrieves the available time zones as a list. + """ + return list(self._vtz.keys()) + + def get(self, tzid=None): + """ + Retrieve a :py:class:`datetime.tzinfo` object by its ``tzid``. + + :param tzid: + If there is exactly one time zone available, omitting ``tzid`` + or passing :py:const:`None` value returns it. Otherwise a valid + key (which can be retrieved from :func:`keys`) is required. + + :raises ValueError: + Raised if ``tzid`` is not specified but there are either more + or fewer than 1 zone defined. + + :returns: + Returns either a :py:class:`datetime.tzinfo` object representing + the relevant time zone or :py:const:`None` if the ``tzid`` was + not found. + """ + if tzid is None: + if len(self._vtz) == 0: + raise ValueError("no timezones defined") + elif len(self._vtz) > 1: + raise ValueError("more than one timezone available") + tzid = next(iter(self._vtz)) + + return self._vtz.get(tzid) + + def _parse_offset(self, s): + s = s.strip() + if not s: + raise ValueError("empty offset") + if s[0] in ('+', '-'): + signal = (-1, +1)[s[0] == '+'] + s = s[1:] + else: + signal = +1 + if len(s) == 4: + return (int(s[:2]) * 3600 + int(s[2:]) * 60) * signal + elif len(s) == 6: + return (int(s[:2]) * 3600 + int(s[2:4]) * 60 + int(s[4:])) * signal + else: + raise ValueError("invalid offset: " + s) + + def _parse_rfc(self, s): + lines = s.splitlines() + if not lines: + raise ValueError("empty string") + + # Unfold + i = 0 + while i < len(lines): + line = lines[i].rstrip() + if not line: + del lines[i] + elif i > 0 and line[0] == " ": + lines[i-1] += line[1:] + del lines[i] + else: + i += 1 + + tzid = None + comps = [] + invtz = False + comptype = None + for line in lines: + if not line: + continue + name, value = line.split(':', 1) + parms = name.split(';') + if not parms: + raise ValueError("empty property name") + name = parms[0].upper() + parms = parms[1:] + if invtz: + if name == "BEGIN": + if value in ("STANDARD", "DAYLIGHT"): + # Process component + pass + else: + raise ValueError("unknown component: "+value) + comptype = value + founddtstart = False + tzoffsetfrom = None + tzoffsetto = None + rrulelines = [] + tzname = None + elif name == "END": + if value == "VTIMEZONE": + if comptype: + raise ValueError("component not closed: "+comptype) + if not tzid: + raise ValueError("mandatory TZID not found") + if not comps: + raise ValueError( + "at least one component is needed") + # Process vtimezone + self._vtz[tzid] = _tzicalvtz(tzid, comps) + invtz = False + elif value == comptype: + if not founddtstart: + raise ValueError("mandatory DTSTART not found") + if tzoffsetfrom is None: + raise ValueError( + "mandatory TZOFFSETFROM not found") + if tzoffsetto is None: + raise ValueError( + "mandatory TZOFFSETFROM not found") + # Process component + rr = None + if rrulelines: + rr = rrule.rrulestr("\n".join(rrulelines), + compatible=True, + ignoretz=True, + cache=True) + comp = _tzicalvtzcomp(tzoffsetfrom, tzoffsetto, + (comptype == "DAYLIGHT"), + tzname, rr) + comps.append(comp) + comptype = None + else: + raise ValueError("invalid component end: "+value) + elif comptype: + if name == "DTSTART": + # DTSTART in VTIMEZONE takes a subset of valid RRULE + # values under RFC 5545. + for parm in parms: + if parm != 'VALUE=DATE-TIME': + msg = ('Unsupported DTSTART param in ' + + 'VTIMEZONE: ' + parm) + raise ValueError(msg) + rrulelines.append(line) + founddtstart = True + elif name in ("RRULE", "RDATE", "EXRULE", "EXDATE"): + rrulelines.append(line) + elif name == "TZOFFSETFROM": + if parms: + raise ValueError( + "unsupported %s parm: %s " % (name, parms[0])) + tzoffsetfrom = self._parse_offset(value) + elif name == "TZOFFSETTO": + if parms: + raise ValueError( + "unsupported TZOFFSETTO parm: "+parms[0]) + tzoffsetto = self._parse_offset(value) + elif name == "TZNAME": + if parms: + raise ValueError( + "unsupported TZNAME parm: "+parms[0]) + tzname = value + elif name == "COMMENT": + pass + else: + raise ValueError("unsupported property: "+name) + else: + if name == "TZID": + if parms: + raise ValueError( + "unsupported TZID parm: "+parms[0]) + tzid = value + elif name in ("TZURL", "LAST-MODIFIED", "COMMENT"): + pass + else: + raise ValueError("unsupported property: "+name) + elif name == "BEGIN" and value == "VTIMEZONE": + tzid = None + comps = [] + invtz = True + + def __repr__(self): + return "%s(%s)" % (self.__class__.__name__, repr(self._s)) + + +if sys.platform != "win32": + TZFILES = ["/etc/localtime", "localtime"] + TZPATHS = ["/usr/share/zoneinfo", + "/usr/lib/zoneinfo", + "/usr/share/lib/zoneinfo", + "/etc/zoneinfo"] +else: + TZFILES = [] + TZPATHS = [] + + +def __get_gettz(): + tzlocal_classes = (tzlocal,) + if tzwinlocal is not None: + tzlocal_classes += (tzwinlocal,) + + class GettzFunc(object): + """ + Retrieve a time zone object from a string representation + + This function is intended to retrieve the :py:class:`tzinfo` subclass + that best represents the time zone that would be used if a POSIX + `TZ variable`_ were set to the same value. + + If no argument or an empty string is passed to ``gettz``, local time + is returned: + + .. code-block:: python3 + + >>> gettz() + tzfile('/etc/localtime') + + This function is also the preferred way to map IANA tz database keys + to :class:`tzfile` objects: + + .. code-block:: python3 + + >>> gettz('Pacific/Kiritimati') + tzfile('/usr/share/zoneinfo/Pacific/Kiritimati') + + On Windows, the standard is extended to include the Windows-specific + zone names provided by the operating system: + + .. code-block:: python3 + + >>> gettz('Egypt Standard Time') + tzwin('Egypt Standard Time') + + Passing a GNU ``TZ`` style string time zone specification returns a + :class:`tzstr` object: + + .. code-block:: python3 + + >>> gettz('AEST-10AEDT-11,M10.1.0/2,M4.1.0/3') + tzstr('AEST-10AEDT-11,M10.1.0/2,M4.1.0/3') + + :param name: + A time zone name (IANA, or, on Windows, Windows keys), location of + a ``tzfile(5)`` zoneinfo file or ``TZ`` variable style time zone + specifier. An empty string, no argument or ``None`` is interpreted + as local time. + + :return: + Returns an instance of one of ``dateutil``'s :py:class:`tzinfo` + subclasses. + + .. versionchanged:: 2.7.0 + + After version 2.7.0, any two calls to ``gettz`` using the same + input strings will return the same object: + + .. code-block:: python3 + + >>> tz.gettz('America/Chicago') is tz.gettz('America/Chicago') + True + + In addition to improving performance, this ensures that + `"same zone" semantics`_ are used for datetimes in the same zone. + + + .. _`TZ variable`: + https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html + + .. _`"same zone" semantics`: + https://blog.ganssle.io/articles/2018/02/aware-datetime-arithmetic.html + """ + def __init__(self): + + self.__instances = weakref.WeakValueDictionary() + self.__strong_cache_size = 8 + self.__strong_cache = OrderedDict() + self._cache_lock = _thread.allocate_lock() + + def __call__(self, name=None): + with self._cache_lock: + rv = self.__instances.get(name, None) + + if rv is None: + rv = self.nocache(name=name) + if not (name is None + or isinstance(rv, tzlocal_classes) + or rv is None): + # tzlocal is slightly more complicated than the other + # time zone providers because it depends on environment + # at construction time, so don't cache that. + # + # We also cannot store weak references to None, so we + # will also not store that. + self.__instances[name] = rv + else: + # No need for strong caching, return immediately + return rv + + self.__strong_cache[name] = self.__strong_cache.pop(name, rv) + + if len(self.__strong_cache) > self.__strong_cache_size: + self.__strong_cache.popitem(last=False) + + return rv + + def set_cache_size(self, size): + with self._cache_lock: + self.__strong_cache_size = size + while len(self.__strong_cache) > size: + self.__strong_cache.popitem(last=False) + + def cache_clear(self): + with self._cache_lock: + self.__instances = weakref.WeakValueDictionary() + self.__strong_cache.clear() + + @staticmethod + def nocache(name=None): + """A non-cached version of gettz""" + tz = None + if not name: + try: + name = os.environ["TZ"] + except KeyError: + pass + if name is None or name in ("", ":"): + for filepath in TZFILES: + if not os.path.isabs(filepath): + filename = filepath + for path in TZPATHS: + filepath = os.path.join(path, filename) + if os.path.isfile(filepath): + break + else: + continue + if os.path.isfile(filepath): + try: + tz = tzfile(filepath) + break + except (IOError, OSError, ValueError): + pass + else: + tz = tzlocal() + else: + try: + if name.startswith(":"): + name = name[1:] + except TypeError as e: + if isinstance(name, bytes): + new_msg = "gettz argument should be str, not bytes" + six.raise_from(TypeError(new_msg), e) + else: + raise + if os.path.isabs(name): + if os.path.isfile(name): + tz = tzfile(name) + else: + tz = None + else: + for path in TZPATHS: + filepath = os.path.join(path, name) + if not os.path.isfile(filepath): + filepath = filepath.replace(' ', '_') + if not os.path.isfile(filepath): + continue + try: + tz = tzfile(filepath) + break + except (IOError, OSError, ValueError): + pass + else: + tz = None + if tzwin is not None: + try: + tz = tzwin(name) + except (WindowsError, UnicodeEncodeError): + # UnicodeEncodeError is for Python 2.7 compat + tz = None + + if not tz: + from dateutil.zoneinfo import get_zonefile_instance + tz = get_zonefile_instance().get(name) + + if not tz: + for c in name: + # name is not a tzstr unless it has at least + # one offset. For short values of "name", an + # explicit for loop seems to be the fastest way + # To determine if a string contains a digit + if c in "0123456789": + try: + tz = tzstr(name) + except ValueError: + pass + break + else: + if name in ("GMT", "UTC"): + tz = UTC + elif name in time.tzname: + tz = tzlocal() + return tz + + return GettzFunc() + + +gettz = __get_gettz() +del __get_gettz + + +def datetime_exists(dt, tz=None): + """ + Given a datetime and a time zone, determine whether or not a given datetime + would fall in a gap. + + :param dt: + A :class:`datetime.datetime` (whose time zone will be ignored if ``tz`` + is provided.) + + :param tz: + A :class:`datetime.tzinfo` with support for the ``fold`` attribute. If + ``None`` or not provided, the datetime's own time zone will be used. + + :return: + Returns a boolean value whether or not the "wall time" exists in + ``tz``. + + .. versionadded:: 2.7.0 + """ + if tz is None: + if dt.tzinfo is None: + raise ValueError('Datetime is naive and no time zone provided.') + tz = dt.tzinfo + + dt = dt.replace(tzinfo=None) + + # This is essentially a test of whether or not the datetime can survive + # a round trip to UTC. + dt_rt = dt.replace(tzinfo=tz).astimezone(UTC).astimezone(tz) + dt_rt = dt_rt.replace(tzinfo=None) + + return dt == dt_rt + + +def datetime_ambiguous(dt, tz=None): + """ + Given a datetime and a time zone, determine whether or not a given datetime + is ambiguous (i.e if there are two times differentiated only by their DST + status). + + :param dt: + A :class:`datetime.datetime` (whose time zone will be ignored if ``tz`` + is provided.) + + :param tz: + A :class:`datetime.tzinfo` with support for the ``fold`` attribute. If + ``None`` or not provided, the datetime's own time zone will be used. + + :return: + Returns a boolean value whether or not the "wall time" is ambiguous in + ``tz``. + + .. versionadded:: 2.6.0 + """ + if tz is None: + if dt.tzinfo is None: + raise ValueError('Datetime is naive and no time zone provided.') + + tz = dt.tzinfo + + # If a time zone defines its own "is_ambiguous" function, we'll use that. + is_ambiguous_fn = getattr(tz, 'is_ambiguous', None) + if is_ambiguous_fn is not None: + try: + return tz.is_ambiguous(dt) + except Exception: + pass + + # If it doesn't come out and tell us it's ambiguous, we'll just check if + # the fold attribute has any effect on this particular date and time. + dt = dt.replace(tzinfo=tz) + wall_0 = enfold(dt, fold=0) + wall_1 = enfold(dt, fold=1) + + same_offset = wall_0.utcoffset() == wall_1.utcoffset() + same_dst = wall_0.dst() == wall_1.dst() + + return not (same_offset and same_dst) + + +def resolve_imaginary(dt): + """ + Given a datetime that may be imaginary, return an existing datetime. + + This function assumes that an imaginary datetime represents what the + wall time would be in a zone had the offset transition not occurred, so + it will always fall forward by the transition's change in offset. + + .. doctest:: + + >>> from dateutil import tz + >>> from datetime import datetime + >>> NYC = tz.gettz('America/New_York') + >>> print(tz.resolve_imaginary(datetime(2017, 3, 12, 2, 30, tzinfo=NYC))) + 2017-03-12 03:30:00-04:00 + + >>> KIR = tz.gettz('Pacific/Kiritimati') + >>> print(tz.resolve_imaginary(datetime(1995, 1, 1, 12, 30, tzinfo=KIR))) + 1995-01-02 12:30:00+14:00 + + As a note, :func:`datetime.astimezone` is guaranteed to produce a valid, + existing datetime, so a round-trip to and from UTC is sufficient to get + an extant datetime, however, this generally "falls back" to an earlier time + rather than falling forward to the STD side (though no guarantees are made + about this behavior). + + :param dt: + A :class:`datetime.datetime` which may or may not exist. + + :return: + Returns an existing :class:`datetime.datetime`. If ``dt`` was not + imaginary, the datetime returned is guaranteed to be the same object + passed to the function. + + .. versionadded:: 2.7.0 + """ + if dt.tzinfo is not None and not datetime_exists(dt): + + curr_offset = (dt + datetime.timedelta(hours=24)).utcoffset() + old_offset = (dt - datetime.timedelta(hours=24)).utcoffset() + + dt += curr_offset - old_offset + + return dt + + +def _datetime_to_timestamp(dt): + """ + Convert a :class:`datetime.datetime` object to an epoch timestamp in + seconds since January 1, 1970, ignoring the time zone. + """ + return (dt.replace(tzinfo=None) - EPOCH).total_seconds() + + +if sys.version_info >= (3, 6): + def _get_supported_offset(second_offset): + return second_offset +else: + def _get_supported_offset(second_offset): + # For python pre-3.6, round to full-minutes if that's not the case. + # Python's datetime doesn't accept sub-minute timezones. Check + # http://python.org/sf/1447945 or https://bugs.python.org/issue5288 + # for some information. + old_offset = second_offset + calculated_offset = 60 * ((second_offset + 30) // 60) + return calculated_offset + + +try: + # Python 3.7 feature + from contextlib import nullcontext as _nullcontext +except ImportError: + class _nullcontext(object): + """ + Class for wrapping contexts so that they are passed through in a + with statement. + """ + def __init__(self, context): + self.context = context + + def __enter__(self): + return self.context + + def __exit__(*args, **kwargs): + pass + +# vim:ts=4:sw=4:et diff --git a/venv/lib/python3.12/site-packages/dateutil/tz/win.py b/venv/lib/python3.12/site-packages/dateutil/tz/win.py new file mode 100644 index 00000000..cde07ba7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/dateutil/tz/win.py @@ -0,0 +1,370 @@ +# -*- coding: utf-8 -*- +""" +This module provides an interface to the native time zone data on Windows, +including :py:class:`datetime.tzinfo` implementations. + +Attempting to import this module on a non-Windows platform will raise an +:py:obj:`ImportError`. +""" +# This code was originally contributed by Jeffrey Harris. +import datetime +import struct + +from six.moves import winreg +from six import text_type + +try: + import ctypes + from ctypes import wintypes +except ValueError: + # ValueError is raised on non-Windows systems for some horrible reason. + raise ImportError("Running tzwin on non-Windows system") + +from ._common import tzrangebase + +__all__ = ["tzwin", "tzwinlocal", "tzres"] + +ONEWEEK = datetime.timedelta(7) + +TZKEYNAMENT = r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones" +TZKEYNAME9X = r"SOFTWARE\Microsoft\Windows\CurrentVersion\Time Zones" +TZLOCALKEYNAME = r"SYSTEM\CurrentControlSet\Control\TimeZoneInformation" + + +def _settzkeyname(): + handle = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) + try: + winreg.OpenKey(handle, TZKEYNAMENT).Close() + TZKEYNAME = TZKEYNAMENT + except WindowsError: + TZKEYNAME = TZKEYNAME9X + handle.Close() + return TZKEYNAME + + +TZKEYNAME = _settzkeyname() + + +class tzres(object): + """ + Class for accessing ``tzres.dll``, which contains timezone name related + resources. + + .. versionadded:: 2.5.0 + """ + p_wchar = ctypes.POINTER(wintypes.WCHAR) # Pointer to a wide char + + def __init__(self, tzres_loc='tzres.dll'): + # Load the user32 DLL so we can load strings from tzres + user32 = ctypes.WinDLL('user32') + + # Specify the LoadStringW function + user32.LoadStringW.argtypes = (wintypes.HINSTANCE, + wintypes.UINT, + wintypes.LPWSTR, + ctypes.c_int) + + self.LoadStringW = user32.LoadStringW + self._tzres = ctypes.WinDLL(tzres_loc) + self.tzres_loc = tzres_loc + + def load_name(self, offset): + """ + Load a timezone name from a DLL offset (integer). + + >>> from dateutil.tzwin import tzres + >>> tzr = tzres() + >>> print(tzr.load_name(112)) + 'Eastern Standard Time' + + :param offset: + A positive integer value referring to a string from the tzres dll. + + .. note:: + + Offsets found in the registry are generally of the form + ``@tzres.dll,-114``. The offset in this case is 114, not -114. + + """ + resource = self.p_wchar() + lpBuffer = ctypes.cast(ctypes.byref(resource), wintypes.LPWSTR) + nchar = self.LoadStringW(self._tzres._handle, offset, lpBuffer, 0) + return resource[:nchar] + + def name_from_string(self, tzname_str): + """ + Parse strings as returned from the Windows registry into the time zone + name as defined in the registry. + + >>> from dateutil.tzwin import tzres + >>> tzr = tzres() + >>> print(tzr.name_from_string('@tzres.dll,-251')) + 'Dateline Daylight Time' + >>> print(tzr.name_from_string('Eastern Standard Time')) + 'Eastern Standard Time' + + :param tzname_str: + A timezone name string as returned from a Windows registry key. + + :return: + Returns the localized timezone string from tzres.dll if the string + is of the form `@tzres.dll,-offset`, else returns the input string. + """ + if not tzname_str.startswith('@'): + return tzname_str + + name_splt = tzname_str.split(',-') + try: + offset = int(name_splt[1]) + except: + raise ValueError("Malformed timezone string.") + + return self.load_name(offset) + + +class tzwinbase(tzrangebase): + """tzinfo class based on win32's timezones available in the registry.""" + def __init__(self): + raise NotImplementedError('tzwinbase is an abstract base class') + + def __eq__(self, other): + # Compare on all relevant dimensions, including name. + if not isinstance(other, tzwinbase): + return NotImplemented + + return (self._std_offset == other._std_offset and + self._dst_offset == other._dst_offset and + self._stddayofweek == other._stddayofweek and + self._dstdayofweek == other._dstdayofweek and + self._stdweeknumber == other._stdweeknumber and + self._dstweeknumber == other._dstweeknumber and + self._stdhour == other._stdhour and + self._dsthour == other._dsthour and + self._stdminute == other._stdminute and + self._dstminute == other._dstminute and + self._std_abbr == other._std_abbr and + self._dst_abbr == other._dst_abbr) + + @staticmethod + def list(): + """Return a list of all time zones known to the system.""" + with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle: + with winreg.OpenKey(handle, TZKEYNAME) as tzkey: + result = [winreg.EnumKey(tzkey, i) + for i in range(winreg.QueryInfoKey(tzkey)[0])] + return result + + def display(self): + """ + Return the display name of the time zone. + """ + return self._display + + def transitions(self, year): + """ + For a given year, get the DST on and off transition times, expressed + always on the standard time side. For zones with no transitions, this + function returns ``None``. + + :param year: + The year whose transitions you would like to query. + + :return: + Returns a :class:`tuple` of :class:`datetime.datetime` objects, + ``(dston, dstoff)`` for zones with an annual DST transition, or + ``None`` for fixed offset zones. + """ + + if not self.hasdst: + return None + + dston = picknthweekday(year, self._dstmonth, self._dstdayofweek, + self._dsthour, self._dstminute, + self._dstweeknumber) + + dstoff = picknthweekday(year, self._stdmonth, self._stddayofweek, + self._stdhour, self._stdminute, + self._stdweeknumber) + + # Ambiguous dates default to the STD side + dstoff -= self._dst_base_offset + + return dston, dstoff + + def _get_hasdst(self): + return self._dstmonth != 0 + + @property + def _dst_base_offset(self): + return self._dst_base_offset_ + + +class tzwin(tzwinbase): + """ + Time zone object created from the zone info in the Windows registry + + These are similar to :py:class:`dateutil.tz.tzrange` objects in that + the time zone data is provided in the format of a single offset rule + for either 0 or 2 time zone transitions per year. + + :param: name + The name of a Windows time zone key, e.g. "Eastern Standard Time". + The full list of keys can be retrieved with :func:`tzwin.list`. + """ + + def __init__(self, name): + self._name = name + + with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle: + tzkeyname = text_type("{kn}\\{name}").format(kn=TZKEYNAME, name=name) + with winreg.OpenKey(handle, tzkeyname) as tzkey: + keydict = valuestodict(tzkey) + + self._std_abbr = keydict["Std"] + self._dst_abbr = keydict["Dlt"] + + self._display = keydict["Display"] + + # See http://ww_winreg.jsiinc.com/SUBA/tip0300/rh0398.htm + tup = struct.unpack("=3l16h", keydict["TZI"]) + stdoffset = -tup[0]-tup[1] # Bias + StandardBias * -1 + dstoffset = stdoffset-tup[2] # + DaylightBias * -1 + self._std_offset = datetime.timedelta(minutes=stdoffset) + self._dst_offset = datetime.timedelta(minutes=dstoffset) + + # for the meaning see the win32 TIME_ZONE_INFORMATION structure docs + # http://msdn.microsoft.com/en-us/library/windows/desktop/ms725481(v=vs.85).aspx + (self._stdmonth, + self._stddayofweek, # Sunday = 0 + self._stdweeknumber, # Last = 5 + self._stdhour, + self._stdminute) = tup[4:9] + + (self._dstmonth, + self._dstdayofweek, # Sunday = 0 + self._dstweeknumber, # Last = 5 + self._dsthour, + self._dstminute) = tup[12:17] + + self._dst_base_offset_ = self._dst_offset - self._std_offset + self.hasdst = self._get_hasdst() + + def __repr__(self): + return "tzwin(%s)" % repr(self._name) + + def __reduce__(self): + return (self.__class__, (self._name,)) + + +class tzwinlocal(tzwinbase): + """ + Class representing the local time zone information in the Windows registry + + While :class:`dateutil.tz.tzlocal` makes system calls (via the :mod:`time` + module) to retrieve time zone information, ``tzwinlocal`` retrieves the + rules directly from the Windows registry and creates an object like + :class:`dateutil.tz.tzwin`. + + Because Windows does not have an equivalent of :func:`time.tzset`, on + Windows, :class:`dateutil.tz.tzlocal` instances will always reflect the + time zone settings *at the time that the process was started*, meaning + changes to the machine's time zone settings during the run of a program + on Windows will **not** be reflected by :class:`dateutil.tz.tzlocal`. + Because ``tzwinlocal`` reads the registry directly, it is unaffected by + this issue. + """ + def __init__(self): + with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle: + with winreg.OpenKey(handle, TZLOCALKEYNAME) as tzlocalkey: + keydict = valuestodict(tzlocalkey) + + self._std_abbr = keydict["StandardName"] + self._dst_abbr = keydict["DaylightName"] + + try: + tzkeyname = text_type('{kn}\\{sn}').format(kn=TZKEYNAME, + sn=self._std_abbr) + with winreg.OpenKey(handle, tzkeyname) as tzkey: + _keydict = valuestodict(tzkey) + self._display = _keydict["Display"] + except OSError: + self._display = None + + stdoffset = -keydict["Bias"]-keydict["StandardBias"] + dstoffset = stdoffset-keydict["DaylightBias"] + + self._std_offset = datetime.timedelta(minutes=stdoffset) + self._dst_offset = datetime.timedelta(minutes=dstoffset) + + # For reasons unclear, in this particular key, the day of week has been + # moved to the END of the SYSTEMTIME structure. + tup = struct.unpack("=8h", keydict["StandardStart"]) + + (self._stdmonth, + self._stdweeknumber, # Last = 5 + self._stdhour, + self._stdminute) = tup[1:5] + + self._stddayofweek = tup[7] + + tup = struct.unpack("=8h", keydict["DaylightStart"]) + + (self._dstmonth, + self._dstweeknumber, # Last = 5 + self._dsthour, + self._dstminute) = tup[1:5] + + self._dstdayofweek = tup[7] + + self._dst_base_offset_ = self._dst_offset - self._std_offset + self.hasdst = self._get_hasdst() + + def __repr__(self): + return "tzwinlocal()" + + def __str__(self): + # str will return the standard name, not the daylight name. + return "tzwinlocal(%s)" % repr(self._std_abbr) + + def __reduce__(self): + return (self.__class__, ()) + + +def picknthweekday(year, month, dayofweek, hour, minute, whichweek): + """ dayofweek == 0 means Sunday, whichweek 5 means last instance """ + first = datetime.datetime(year, month, 1, hour, minute) + + # This will work if dayofweek is ISO weekday (1-7) or Microsoft-style (0-6), + # Because 7 % 7 = 0 + weekdayone = first.replace(day=((dayofweek - first.isoweekday()) % 7) + 1) + wd = weekdayone + ((whichweek - 1) * ONEWEEK) + if (wd.month != month): + wd -= ONEWEEK + + return wd + + +def valuestodict(key): + """Convert a registry key's values to a dictionary.""" + dout = {} + size = winreg.QueryInfoKey(key)[1] + tz_res = None + + for i in range(size): + key_name, value, dtype = winreg.EnumValue(key, i) + if dtype == winreg.REG_DWORD or dtype == winreg.REG_DWORD_LITTLE_ENDIAN: + # If it's a DWORD (32-bit integer), it's stored as unsigned - convert + # that to a proper signed integer + if value & (1 << 31): + value = value - (1 << 32) + elif dtype == winreg.REG_SZ: + # If it's a reference to the tzres DLL, load the actual string + if value.startswith('@tzres'): + tz_res = tz_res or tzres() + value = tz_res.name_from_string(value) + + value = value.rstrip('\x00') # Remove trailing nulls + + dout[key_name] = value + + return dout diff --git a/venv/lib/python3.12/site-packages/dateutil/tzwin.py b/venv/lib/python3.12/site-packages/dateutil/tzwin.py new file mode 100644 index 00000000..cebc673e --- /dev/null +++ b/venv/lib/python3.12/site-packages/dateutil/tzwin.py @@ -0,0 +1,2 @@ +# tzwin has moved to dateutil.tz.win +from .tz.win import * diff --git a/venv/lib/python3.12/site-packages/dateutil/utils.py b/venv/lib/python3.12/site-packages/dateutil/utils.py new file mode 100644 index 00000000..dd2d245a --- /dev/null +++ b/venv/lib/python3.12/site-packages/dateutil/utils.py @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- +""" +This module offers general convenience and utility functions for dealing with +datetimes. + +.. versionadded:: 2.7.0 +""" +from __future__ import unicode_literals + +from datetime import datetime, time + + +def today(tzinfo=None): + """ + Returns a :py:class:`datetime` representing the current day at midnight + + :param tzinfo: + The time zone to attach (also used to determine the current day). + + :return: + A :py:class:`datetime.datetime` object representing the current day + at midnight. + """ + + dt = datetime.now(tzinfo) + return datetime.combine(dt.date(), time(0, tzinfo=tzinfo)) + + +def default_tzinfo(dt, tzinfo): + """ + Sets the ``tzinfo`` parameter on naive datetimes only + + This is useful for example when you are provided a datetime that may have + either an implicit or explicit time zone, such as when parsing a time zone + string. + + .. doctest:: + + >>> from dateutil.tz import tzoffset + >>> from dateutil.parser import parse + >>> from dateutil.utils import default_tzinfo + >>> dflt_tz = tzoffset("EST", -18000) + >>> print(default_tzinfo(parse('2014-01-01 12:30 UTC'), dflt_tz)) + 2014-01-01 12:30:00+00:00 + >>> print(default_tzinfo(parse('2014-01-01 12:30'), dflt_tz)) + 2014-01-01 12:30:00-05:00 + + :param dt: + The datetime on which to replace the time zone + + :param tzinfo: + The :py:class:`datetime.tzinfo` subclass instance to assign to + ``dt`` if (and only if) it is naive. + + :return: + Returns an aware :py:class:`datetime.datetime`. + """ + if dt.tzinfo is not None: + return dt + else: + return dt.replace(tzinfo=tzinfo) + + +def within_delta(dt1, dt2, delta): + """ + Useful for comparing two datetimes that may have a negligible difference + to be considered equal. + """ + delta = abs(delta) + difference = dt1 - dt2 + return -delta <= difference <= delta diff --git a/venv/lib/python3.12/site-packages/dateutil/zoneinfo/__init__.py b/venv/lib/python3.12/site-packages/dateutil/zoneinfo/__init__.py new file mode 100644 index 00000000..34f11ad6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/dateutil/zoneinfo/__init__.py @@ -0,0 +1,167 @@ +# -*- coding: utf-8 -*- +import warnings +import json + +from tarfile import TarFile +from pkgutil import get_data +from io import BytesIO + +from dateutil.tz import tzfile as _tzfile + +__all__ = ["get_zonefile_instance", "gettz", "gettz_db_metadata"] + +ZONEFILENAME = "dateutil-zoneinfo.tar.gz" +METADATA_FN = 'METADATA' + + +class tzfile(_tzfile): + def __reduce__(self): + return (gettz, (self._filename,)) + + +def getzoneinfofile_stream(): + try: + return BytesIO(get_data(__name__, ZONEFILENAME)) + except IOError as e: # TODO switch to FileNotFoundError? + warnings.warn("I/O error({0}): {1}".format(e.errno, e.strerror)) + return None + + +class ZoneInfoFile(object): + def __init__(self, zonefile_stream=None): + if zonefile_stream is not None: + with TarFile.open(fileobj=zonefile_stream) as tf: + self.zones = {zf.name: tzfile(tf.extractfile(zf), filename=zf.name) + for zf in tf.getmembers() + if zf.isfile() and zf.name != METADATA_FN} + # deal with links: They'll point to their parent object. Less + # waste of memory + links = {zl.name: self.zones[zl.linkname] + for zl in tf.getmembers() if + zl.islnk() or zl.issym()} + self.zones.update(links) + try: + metadata_json = tf.extractfile(tf.getmember(METADATA_FN)) + metadata_str = metadata_json.read().decode('UTF-8') + self.metadata = json.loads(metadata_str) + except KeyError: + # no metadata in tar file + self.metadata = None + else: + self.zones = {} + self.metadata = None + + def get(self, name, default=None): + """ + Wrapper for :func:`ZoneInfoFile.zones.get`. This is a convenience method + for retrieving zones from the zone dictionary. + + :param name: + The name of the zone to retrieve. (Generally IANA zone names) + + :param default: + The value to return in the event of a missing key. + + .. versionadded:: 2.6.0 + + """ + return self.zones.get(name, default) + + +# The current API has gettz as a module function, although in fact it taps into +# a stateful class. So as a workaround for now, without changing the API, we +# will create a new "global" class instance the first time a user requests a +# timezone. Ugly, but adheres to the api. +# +# TODO: Remove after deprecation period. +_CLASS_ZONE_INSTANCE = [] + + +def get_zonefile_instance(new_instance=False): + """ + This is a convenience function which provides a :class:`ZoneInfoFile` + instance using the data provided by the ``dateutil`` package. By default, it + caches a single instance of the ZoneInfoFile object and returns that. + + :param new_instance: + If ``True``, a new instance of :class:`ZoneInfoFile` is instantiated and + used as the cached instance for the next call. Otherwise, new instances + are created only as necessary. + + :return: + Returns a :class:`ZoneInfoFile` object. + + .. versionadded:: 2.6 + """ + if new_instance: + zif = None + else: + zif = getattr(get_zonefile_instance, '_cached_instance', None) + + if zif is None: + zif = ZoneInfoFile(getzoneinfofile_stream()) + + get_zonefile_instance._cached_instance = zif + + return zif + + +def gettz(name): + """ + This retrieves a time zone from the local zoneinfo tarball that is packaged + with dateutil. + + :param name: + An IANA-style time zone name, as found in the zoneinfo file. + + :return: + Returns a :class:`dateutil.tz.tzfile` time zone object. + + .. warning:: + It is generally inadvisable to use this function, and it is only + provided for API compatibility with earlier versions. This is *not* + equivalent to ``dateutil.tz.gettz()``, which selects an appropriate + time zone based on the inputs, favoring system zoneinfo. This is ONLY + for accessing the dateutil-specific zoneinfo (which may be out of + date compared to the system zoneinfo). + + .. deprecated:: 2.6 + If you need to use a specific zoneinfofile over the system zoneinfo, + instantiate a :class:`dateutil.zoneinfo.ZoneInfoFile` object and call + :func:`dateutil.zoneinfo.ZoneInfoFile.get(name)` instead. + + Use :func:`get_zonefile_instance` to retrieve an instance of the + dateutil-provided zoneinfo. + """ + warnings.warn("zoneinfo.gettz() will be removed in future versions, " + "to use the dateutil-provided zoneinfo files, instantiate a " + "ZoneInfoFile object and use ZoneInfoFile.zones.get() " + "instead. See the documentation for details.", + DeprecationWarning) + + if len(_CLASS_ZONE_INSTANCE) == 0: + _CLASS_ZONE_INSTANCE.append(ZoneInfoFile(getzoneinfofile_stream())) + return _CLASS_ZONE_INSTANCE[0].zones.get(name) + + +def gettz_db_metadata(): + """ Get the zonefile metadata + + See `zonefile_metadata`_ + + :returns: + A dictionary with the database metadata + + .. deprecated:: 2.6 + See deprecation warning in :func:`zoneinfo.gettz`. To get metadata, + query the attribute ``zoneinfo.ZoneInfoFile.metadata``. + """ + warnings.warn("zoneinfo.gettz_db_metadata() will be removed in future " + "versions, to use the dateutil-provided zoneinfo files, " + "ZoneInfoFile object and query the 'metadata' attribute " + "instead. See the documentation for details.", + DeprecationWarning) + + if len(_CLASS_ZONE_INSTANCE) == 0: + _CLASS_ZONE_INSTANCE.append(ZoneInfoFile(getzoneinfofile_stream())) + return _CLASS_ZONE_INSTANCE[0].metadata diff --git a/venv/lib/python3.12/site-packages/dateutil/zoneinfo/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/dateutil/zoneinfo/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 00000000..4b06b9ca Binary files /dev/null and b/venv/lib/python3.12/site-packages/dateutil/zoneinfo/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/dateutil/zoneinfo/__pycache__/rebuild.cpython-312.pyc b/venv/lib/python3.12/site-packages/dateutil/zoneinfo/__pycache__/rebuild.cpython-312.pyc new file mode 100644 index 00000000..3efee15a Binary files /dev/null and b/venv/lib/python3.12/site-packages/dateutil/zoneinfo/__pycache__/rebuild.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz b/venv/lib/python3.12/site-packages/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz new file mode 100644 index 00000000..1461f8c8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz differ diff --git a/venv/lib/python3.12/site-packages/dateutil/zoneinfo/rebuild.py b/venv/lib/python3.12/site-packages/dateutil/zoneinfo/rebuild.py new file mode 100644 index 00000000..684c6586 --- /dev/null +++ b/venv/lib/python3.12/site-packages/dateutil/zoneinfo/rebuild.py @@ -0,0 +1,75 @@ +import logging +import os +import tempfile +import shutil +import json +from subprocess import check_call, check_output +from tarfile import TarFile + +from dateutil.zoneinfo import METADATA_FN, ZONEFILENAME + + +def rebuild(filename, tag=None, format="gz", zonegroups=[], metadata=None): + """Rebuild the internal timezone info in dateutil/zoneinfo/zoneinfo*tar* + + filename is the timezone tarball from ``ftp.iana.org/tz``. + + """ + tmpdir = tempfile.mkdtemp() + zonedir = os.path.join(tmpdir, "zoneinfo") + moduledir = os.path.dirname(__file__) + try: + with TarFile.open(filename) as tf: + for name in zonegroups: + tf.extract(name, tmpdir) + filepaths = [os.path.join(tmpdir, n) for n in zonegroups] + + _run_zic(zonedir, filepaths) + + # write metadata file + with open(os.path.join(zonedir, METADATA_FN), 'w') as f: + json.dump(metadata, f, indent=4, sort_keys=True) + target = os.path.join(moduledir, ZONEFILENAME) + with TarFile.open(target, "w:%s" % format) as tf: + for entry in os.listdir(zonedir): + entrypath = os.path.join(zonedir, entry) + tf.add(entrypath, entry) + finally: + shutil.rmtree(tmpdir) + + +def _run_zic(zonedir, filepaths): + """Calls the ``zic`` compiler in a compatible way to get a "fat" binary. + + Recent versions of ``zic`` default to ``-b slim``, while older versions + don't even have the ``-b`` option (but default to "fat" binaries). The + current version of dateutil does not support Version 2+ TZif files, which + causes problems when used in conjunction with "slim" binaries, so this + function is used to ensure that we always get a "fat" binary. + """ + + try: + help_text = check_output(["zic", "--help"]) + except OSError as e: + _print_on_nosuchfile(e) + raise + + if b"-b " in help_text: + bloat_args = ["-b", "fat"] + else: + bloat_args = [] + + check_call(["zic"] + bloat_args + ["-d", zonedir] + filepaths) + + +def _print_on_nosuchfile(e): + """Print helpful troubleshooting message + + e is an exception raised by subprocess.check_call() + + """ + if e.errno == 2: + logging.error( + "Could not find zic. Perhaps you need to install " + "libc-bin or some other package that provides it, " + "or it's not in your PATH?") diff --git a/venv/lib/python3.12/site-packages/ghp_import-2.1.0.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/ghp_import-2.1.0.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/ghp_import-2.1.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.12/site-packages/ghp_import-2.1.0.dist-info/LICENSE b/venv/lib/python3.12/site-packages/ghp_import-2.1.0.dist-info/LICENSE new file mode 100644 index 00000000..756d048c --- /dev/null +++ b/venv/lib/python3.12/site-packages/ghp_import-2.1.0.dist-info/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [2020] [Paul Davis ] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/venv/lib/python3.12/site-packages/ghp_import-2.1.0.dist-info/METADATA b/venv/lib/python3.12/site-packages/ghp_import-2.1.0.dist-info/METADATA new file mode 100644 index 00000000..70c12664 --- /dev/null +++ b/venv/lib/python3.12/site-packages/ghp_import-2.1.0.dist-info/METADATA @@ -0,0 +1,154 @@ +Metadata-Version: 2.1 +Name: ghp-import +Version: 2.1.0 +Summary: Copy your docs directly to the gh-pages branch. +Home-page: https://github.com/c-w/ghp-import +Author: Paul Joseph Davis +Author-email: paul.joseph.davis@gmail.com +License: Apache Software License +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Intended Audience :: Developers +Classifier: Natural Language :: English +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 3 +Description-Content-Type: text/markdown +License-File: LICENSE +Requires-Dist: python-dateutil (>=2.8.1) +Provides-Extra: dev +Requires-Dist: twine ; extra == 'dev' +Requires-Dist: markdown ; extra == 'dev' +Requires-Dist: flake8 ; extra == 'dev' +Requires-Dist: wheel ; extra == 'dev' + +GitHub Pages Import +=================== + +[![CI status](https://github.com/davisp/ghp-import/workflows/CI/badge.svg)](https://github.com/davisp/ghp-import/actions?query=workflow%3Aci) +[![CircleCI](https://circleci.com/gh/c-w/ghp-import/tree/master.svg?style=svg)](https://circleci.com/gh/c-w/ghp-import/tree/master) +[![TravisCI](https://travis-ci.org/c-w/ghp-import.svg?branch=master)](https://travis-ci.org/c-w/ghp-import) + +[![License](https://img.shields.io/badge/License-Apache%202.0-green.svg)](https://opensource.org/licenses/Apache-2.0) +[![Version](https://img.shields.io/pypi/v/ghp-import.svg)](https://pypi.org/project/ghp-import/) + +As part of [gunicorn][gunicorn], [Benoit Chesneau][benoit] and [Paul Davis][davisp] +were looking at how to host documentation. There's the obvious method of +using [GitHub's post-receive hook][github-post] to trigger doc builds and rsync +to a webserver, but we ended up wanting to try out github's hosting to make the +whole interface a bit more robust. + +[GitHub Pages][gh-pages] is a pretty awesome service that GitHub provides for +hosting project documentation. The only thing is that it requires a +`gh-pages` branch that is the site's document root. This means that keeping +documentation sources in the branch with code is a bit difficult. And it really +turns into a head scratcher for things like [Sphinx][sphinx] that want to +access documentation sources and code sources at the same time. + +Then we stumbled across an interesting looking package called +[github-tools][github-tools] that looked almost like what we wanted. It was a tad +complicated and more involved than we wanted but it gave us an idea. Why not +just write a script that can copy a directory to the `gh-pages` branch of the +repository. This saves us from even having to think about the branch and +everything becomes magical. + +This is what `ghp-import` was written for. + +[gunicorn]: http://www.gunicorn.com/ "Gunicorn" +[benoit]: http://github.com/benoitc "Benoît Chesneau" +[davisp]: http://github.com/davisp "Paul J. Davis" +[github-post]: https://help.github.com/articles/post-receive-hooks "GitHub Post-Receive Hook" +[gh-pages]: http://pages.github.com/ "GitHub Pages" +[sphinx]: http://sphinx.pocoo.org/ "Sphinx Documentation" +[github-tools]: http://dinoboff.github.com/github-tools/ "github-tools" + + +Big Fat Warning +--------------- + +This will **DESTROY** your `gh-pages` branch. If you love it, you'll want to +take backups before playing with this. This script assumes that `gh-pages` is +100% derivative. You should never edit files in your `gh-pages` branch by hand +if you're using this script because you will lose your work. + +When used with a prefix, only files below the set prefix will be destroyed, limiting the +above warning to just that directory and everything below it. + +Usage +----- + +``` +Usage: ghp-import [OPTIONS] DIRECTORY + +Options: + -n, --no-jekyll Include a .nojekyll file in the branch. + -c CNAME, --cname=CNAME + Write a CNAME file with the given CNAME. + -m MESG, --message=MESG + The commit message to use on the target branch. + -p, --push Push the branch to origin/{branch} after committing. + -x PREFIX, --prefix=PREFIX + The prefix to add to each file that gets pushed to the + remote. Only files below this prefix will be cleared + out. [none] + -f, --force Force the push to the repository. + -o, --no-history Force new commit without parent history. + -r REMOTE, --remote=REMOTE + The name of the remote to push to. [origin] + -b BRANCH, --branch=BRANCH + Name of the branch to write to. [gh-pages] + -s, --shell Use the shell when invoking Git. [False] + -l, --follow-links Follow symlinks when adding files. [False] + -h, --help show this help message and exit +``` + +Its pretty simple. Inside your repository just run `ghp-import $DOCS_DIR` +where `$DOCS_DIR` is the path to the **built** documentation. This will write a +commit to your `gh-pages` branch with the current documents in it. + +If you specify `-p` it will also attempt to push the `gh-pages` branch to +GitHub. By default it'll just run `git push origin gh-pages`. You can specify +a different remote using the `-r` flag. + +The `-o` option will discard any previous history and ensure that only a +single commit is always pushed to the `gh-pages` branch. This is useful to +avoid bloating the repository size and is **highly recommended**. + +You can specify a different branch with `-b`. This is useful for user and +organization page, which are served from the `master` branch. + +Some Windows users report needing to pass Git commands through the shell which can be accomplished by passing `-s`. + +The `-l` option will cause the import to follow symlinks for users that have odd configurations that include symlinking outside of their documentation directory. + +Python Usage +------------ + +You can also call ghp_import directly from your Python code as a library. The +library has one public function `ghp_import.ghp_import`, which accepts the +following arguments: + +* `srcdir`: The path to the **built** documentation (required). +* `remote`: The name of the remote to push to. Default: `origin`. +* `branch`: Name of the branch to write to. Default: `gh-pages`. +* `mesg`: The commit message to use on the target branch. Default: `Update documentation`. +* `push`: Push the branch to {remote}/{branch} after committing. Default: `False`. +* `prefix`: The prefix to add to each file that gets pushed to the remote. Default: `None`. +* `force`: Force the push to the repository. Default: `False`. +* `no_history`: Force new commit without parent history. Default: `False`. +* `use_shell`: Default: Use the shell when invoking Git. `False`. +* `followlinks`: Follow symlinks when adding files. Default: `False`. +* `cname`: Write a CNAME file with the given CNAME. Default: `None`. +* `nojekyll`: Include a .nojekyll file in the branch. Default: `False`. + +With Python's current working directory (cwd) inside your repository, do the +following: + +```python +from ghp_import import ghp_import +ghp_import('docs', push=True, cname='example.com') +``` + + diff --git a/venv/lib/python3.12/site-packages/ghp_import-2.1.0.dist-info/RECORD b/venv/lib/python3.12/site-packages/ghp_import-2.1.0.dist-info/RECORD new file mode 100644 index 00000000..ab37b16a --- /dev/null +++ b/venv/lib/python3.12/site-packages/ghp_import-2.1.0.dist-info/RECORD @@ -0,0 +1,10 @@ +../../../bin/ghp-import,sha256=-L16NxprEjuaWkMX6BXjt3iapDsY8yb8s0X6rJ8DpJs,250 +__pycache__/ghp_import.cpython-312.pyc,, +ghp_import-2.1.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +ghp_import-2.1.0.dist-info/LICENSE,sha256=C8j_tF8m7dHNDeT1BCWuLLRsWMbYrBE5hNQSC-NVr6k,11374 +ghp_import-2.1.0.dist-info/METADATA,sha256=PCrYmDTJ2XjIuUkYM33d1t8Fva95SG2UphvN-t9b6y8,7177 +ghp_import-2.1.0.dist-info/RECORD,, +ghp_import-2.1.0.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92 +ghp_import-2.1.0.dist-info/entry_points.txt,sha256=mk55YA2cS0KmQK9APJFk_1ny9zFWFOZDtOBZMO0cu1Y,48 +ghp_import-2.1.0.dist-info/top_level.txt,sha256=QGVcxjaCFAMEV3ZX7ADAlIMIlsiyfplHNQi-JwrTgow,11 +ghp_import.py,sha256=zvDcFrdka_GzgEkD1BjJrBfDHD3sCExSbnJHmBE1igU,9234 diff --git a/venv/lib/python3.12/site-packages/ghp_import-2.1.0.dist-info/WHEEL b/venv/lib/python3.12/site-packages/ghp_import-2.1.0.dist-info/WHEEL new file mode 100644 index 00000000..becc9a66 --- /dev/null +++ b/venv/lib/python3.12/site-packages/ghp_import-2.1.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.37.1) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/venv/lib/python3.12/site-packages/ghp_import-2.1.0.dist-info/entry_points.txt b/venv/lib/python3.12/site-packages/ghp_import-2.1.0.dist-info/entry_points.txt new file mode 100644 index 00000000..6f2959a8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/ghp_import-2.1.0.dist-info/entry_points.txt @@ -0,0 +1,3 @@ +[console_scripts] +ghp-import = ghp_import:main + diff --git a/venv/lib/python3.12/site-packages/ghp_import-2.1.0.dist-info/top_level.txt b/venv/lib/python3.12/site-packages/ghp_import-2.1.0.dist-info/top_level.txt new file mode 100644 index 00000000..a780cead --- /dev/null +++ b/venv/lib/python3.12/site-packages/ghp_import-2.1.0.dist-info/top_level.txt @@ -0,0 +1 @@ +ghp_import diff --git a/venv/lib/python3.12/site-packages/ghp_import.py b/venv/lib/python3.12/site-packages/ghp_import.py new file mode 100644 index 00000000..328422bd --- /dev/null +++ b/venv/lib/python3.12/site-packages/ghp_import.py @@ -0,0 +1,306 @@ +#! /usr/bin/env python + +import errno +import os +import subprocess as sp +import sys +import time +from dateutil import tz +from datetime import datetime + +try: + from shlex import quote +except ImportError: + from pipes import quote + +__all__ = ['ghp_import'] +__version__ = "2.1.0" + + +class GhpError(Exception): + def __init__(self, message): + self.message = message + + +if sys.version_info[0] == 3: + def enc(text): + if isinstance(text, bytes): + return text + return text.encode() + + def dec(text): + if isinstance(text, bytes): + return text.decode('utf-8') + return text + + def write(pipe, data): + try: + pipe.stdin.write(data) + except IOError as e: + if e.errno != errno.EPIPE: + raise +else: + def enc(text): + if isinstance(text, unicode): # noqa F821 + return text.encode('utf-8') + return text + + def dec(text): + if isinstance(text, unicode): # noqa F821 + return text + return text.decode('utf-8') + + def write(pipe, data): + pipe.stdin.write(data) + + +class Git(object): + def __init__(self, use_shell=False): + self.use_shell = use_shell + + self.cmd = None + self.pipe = None + self.stderr = None + self.stdout = None + + def check_repo(self): + if self.call('rev-parse') != 0: + error = self.stderr + if not error: + error = "Unknown Git error" + error = dec(error) + if error.startswith("fatal: "): + error = error[len("fatal: "):] + raise GhpError(error) + + def try_rebase(self, remote, branch, no_history=False): + rc = self.call('rev-list', '--max-count=1', '%s/%s' % (remote, branch)) + if rc != 0: + return True + rev = dec(self.stdout.strip()) + if no_history: + rc = self.call('update-ref', '-d', 'refs/heads/%s' % branch) + else: + rc = self.call('update-ref', 'refs/heads/%s' % branch, rev) + if rc != 0: + return False + return True + + def get_config(self, key): + self.call('config', key) + return self.stdout.strip() + + def get_prev_commit(self, branch): + rc = self.call('rev-list', '--max-count=1', branch, '--') + if rc != 0: + return None + return dec(self.stdout).strip() + + def open(self, *args, **kwargs): + if self.use_shell: + self.cmd = 'git ' + ' '.join(map(quote, args)) + else: + self.cmd = ['git'] + list(args) + if sys.version_info >= (3, 2, 0): + kwargs['universal_newlines'] = False + for k in 'stdin stdout stderr'.split(): + kwargs.setdefault(k, sp.PIPE) + kwargs['shell'] = self.use_shell + self.pipe = sp.Popen(self.cmd, **kwargs) + return self.pipe + + def call(self, *args, **kwargs): + self.open(*args, **kwargs) + (self.stdout, self.stderr) = self.pipe.communicate() + return self.pipe.wait() + + def check_call(self, *args, **kwargs): + kwargs["shell"] = self.use_shell + sp.check_call(['git'] + list(args), **kwargs) + + +def mk_when(timestamp=None): + if timestamp is None: + timestamp = int(time.time()) + currtz = datetime.now(tz.tzlocal()).strftime('%z') + return "%s %s" % (timestamp, currtz) + + +def start_commit(pipe, git, branch, message, prefix=None): + uname = os.getenv('GIT_COMMITTER_NAME', dec(git.get_config('user.name'))) + email = os.getenv('GIT_COMMITTER_EMAIL', dec(git.get_config('user.email'))) + when = os.getenv('GIT_COMMITTER_DATE', mk_when()) + write(pipe, enc('commit refs/heads/%s\n' % branch)) + write(pipe, enc('committer %s <%s> %s\n' % (uname, email, when))) + write(pipe, enc('data %d\n%s\n' % (len(enc(message)), message))) + head = git.get_prev_commit(branch) + if head: + write(pipe, enc('from %s\n' % head)) + if prefix: + write(pipe, enc('D %s\n' % prefix)) + else: + write(pipe, enc('deleteall\n')) + + +def add_file(pipe, srcpath, tgtpath): + with open(srcpath, "rb") as handle: + if os.access(srcpath, os.X_OK): + write(pipe, enc('M 100755 inline %s\n' % tgtpath)) + else: + write(pipe, enc('M 100644 inline %s\n' % tgtpath)) + data = handle.read() + write(pipe, enc('data %d\n' % len(data))) + write(pipe, enc(data)) + write(pipe, enc('\n')) + + +def add_nojekyll(pipe, prefix=None): + if prefix: + fpath = os.path.join(prefix, '.nojekyll') + else: + fpath = '.nojekyll' + write(pipe, enc('M 100644 inline %s\n' % fpath)) + write(pipe, enc('data 0\n')) + write(pipe, enc('\n')) + + +def add_cname(pipe, cname): + write(pipe, enc('M 100644 inline CNAME\n')) + write(pipe, enc('data %d\n%s\n' % (len(enc(cname)), cname))) + + +def gitpath(fname): + norm = os.path.normpath(fname) + return "/".join(norm.split(os.path.sep)) + + +def run_import(git, srcdir, **opts): + srcdir = dec(srcdir) + pipe = git.open('fast-import', '--date-format=rfc2822', '--quiet', + stdin=sp.PIPE, stdout=None, stderr=None) + start_commit(pipe, git, opts['branch'], opts['mesg'], opts['prefix']) + for path, _, fnames in os.walk(srcdir, followlinks=opts['followlinks']): + for fn in fnames: + fpath = os.path.join(path, fn) + gpath = gitpath(os.path.relpath(fpath, start=srcdir)) + if opts['prefix']: + gpath = os.path.join(opts['prefix'], gpath) + add_file(pipe, fpath, gpath) + if opts['nojekyll']: + add_nojekyll(pipe, opts['prefix']) + if opts['cname'] is not None: + add_cname(pipe, opts['cname']) + write(pipe, enc('\n')) + pipe.stdin.close() + if pipe.wait() != 0: + sys.stdout.write(enc("Failed to process commit.\n")) + + +def options(): + return [ + (('-n', '--no-jekyll'), dict( + dest='nojekyll', + default=False, + action="store_true", + help='Include a .nojekyll file in the branch.', + )), + (('-c', '--cname'), dict( + dest='cname', + default=None, + help='Write a CNAME file with the given CNAME.', + )), + (('-m', '--message'), dict( + dest='mesg', + default='Update documentation', + help='The commit message to use on the target branch.', + )), + (('-p', '--push'), dict( + dest='push', + default=False, + action='store_true', + help='Push the branch to origin/{branch} after committing.', + )), + (('-x', '--prefix'), dict( + dest='prefix', + default=None, + help='The prefix to add to each file that gets pushed to the ' + 'remote. Only files below this prefix will be cleared ' + 'out. [%(default)s]', + )), + (('-f', '--force'), dict( + dest='force', + default=False, action='store_true', + help='Force the push to the repository.', + )), + (('-o', '--no-history'), dict( + dest='no_history', + default=False, + action='store_true', + help='Force new commit without parent history.', + )), + (('-r', '--remote'), dict( + dest='remote', + default='origin', + help='The name of the remote to push to. [%(default)s]', + )), + (('-b', '--branch'), dict( + dest='branch', + default='gh-pages', + help='Name of the branch to write to. [%(default)s]', + )), + (('-s', '--shell'), dict( + dest='use_shell', + default=False, + action='store_true', + help='Use the shell when invoking Git. [%(default)s]', + )), + (('-l', '--follow-links'), dict( + dest='followlinks', + default=False, + action='store_true', + help='Follow symlinks when adding files. [%(default)s]', + )) + ] + + +def ghp_import(srcdir, **kwargs): + if not os.path.isdir(srcdir): + raise GhpError("Not a directory: %s" % srcdir) + + opts = {kwargs["dest"]: kwargs["default"] for _, kwargs in options()} + opts.update(kwargs) + + git = Git(use_shell=opts['use_shell']) + git.check_repo() + + if not git.try_rebase(opts['remote'], opts['branch'], opts['no_history']): + raise GhpError("Failed to rebase %s branch." % opts['branch']) + + run_import(git, srcdir, **opts) + + if opts['push']: + if opts['force'] or opts['no_history']: + git.check_call('push', opts['remote'], opts['branch'], '--force') + else: + git.check_call('push', opts['remote'], opts['branch']) + + +def main(): + from argparse import ArgumentParser + + parser = ArgumentParser() + parser.add_argument("--version", action="version", version=__version__) + parser.add_argument("directory") + for args, kwargs in options(): + parser.add_argument(*args, **kwargs) + + args = parser.parse_args().__dict__ + + try: + ghp_import(args.pop("directory"), **args) + except GhpError as e: + parser.error(e.message) + + +if __name__ == '__main__': + main() diff --git a/venv/lib/python3.12/site-packages/idna-3.10.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/idna-3.10.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/idna-3.10.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.12/site-packages/idna-3.10.dist-info/LICENSE.md b/venv/lib/python3.12/site-packages/idna-3.10.dist-info/LICENSE.md new file mode 100644 index 00000000..19b6b452 --- /dev/null +++ b/venv/lib/python3.12/site-packages/idna-3.10.dist-info/LICENSE.md @@ -0,0 +1,31 @@ +BSD 3-Clause License + +Copyright (c) 2013-2024, Kim Davies and contributors. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/lib/python3.12/site-packages/idna-3.10.dist-info/METADATA b/venv/lib/python3.12/site-packages/idna-3.10.dist-info/METADATA new file mode 100644 index 00000000..c42623e9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/idna-3.10.dist-info/METADATA @@ -0,0 +1,250 @@ +Metadata-Version: 2.1 +Name: idna +Version: 3.10 +Summary: Internationalized Domain Names in Applications (IDNA) +Author-email: Kim Davies +Requires-Python: >=3.6 +Description-Content-Type: text/x-rst +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: System Administrators +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Internet :: Name Service (DNS) +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: Utilities +Requires-Dist: ruff >= 0.6.2 ; extra == "all" +Requires-Dist: mypy >= 1.11.2 ; extra == "all" +Requires-Dist: pytest >= 8.3.2 ; extra == "all" +Requires-Dist: flake8 >= 7.1.1 ; extra == "all" +Project-URL: Changelog, https://github.com/kjd/idna/blob/master/HISTORY.rst +Project-URL: Issue tracker, https://github.com/kjd/idna/issues +Project-URL: Source, https://github.com/kjd/idna +Provides-Extra: all + +Internationalized Domain Names in Applications (IDNA) +===================================================== + +Support for the Internationalized Domain Names in +Applications (IDNA) protocol as specified in `RFC 5891 +`_. This is the latest version of +the protocol and is sometimes referred to as “IDNA 2008”. + +This library also provides support for Unicode Technical +Standard 46, `Unicode IDNA Compatibility Processing +`_. + +This acts as a suitable replacement for the “encodings.idna” +module that comes with the Python standard library, but which +only supports the older superseded IDNA specification (`RFC 3490 +`_). + +Basic functions are simply executed: + +.. code-block:: pycon + + >>> import idna + >>> idna.encode('ドメイン.テスト') + b'xn--eckwd4c7c.xn--zckzah' + >>> print(idna.decode('xn--eckwd4c7c.xn--zckzah')) + ドメイン.テスト + + +Installation +------------ + +This package is available for installation from PyPI: + +.. code-block:: bash + + $ python3 -m pip install idna + + +Usage +----- + +For typical usage, the ``encode`` and ``decode`` functions will take a +domain name argument and perform a conversion to A-labels or U-labels +respectively. + +.. code-block:: pycon + + >>> import idna + >>> idna.encode('ドメイン.テスト') + b'xn--eckwd4c7c.xn--zckzah' + >>> print(idna.decode('xn--eckwd4c7c.xn--zckzah')) + ドメイン.テスト + +You may use the codec encoding and decoding methods using the +``idna.codec`` module: + +.. code-block:: pycon + + >>> import idna.codec + >>> print('домен.испытание'.encode('idna2008')) + b'xn--d1acufc.xn--80akhbyknj4f' + >>> print(b'xn--d1acufc.xn--80akhbyknj4f'.decode('idna2008')) + домен.испытание + +Conversions can be applied at a per-label basis using the ``ulabel`` or +``alabel`` functions if necessary: + +.. code-block:: pycon + + >>> idna.alabel('测试') + b'xn--0zwm56d' + +Compatibility Mapping (UTS #46) ++++++++++++++++++++++++++++++++ + +As described in `RFC 5895 `_, the +IDNA specification does not normalize input from different potential +ways a user may input a domain name. This functionality, known as +a “mapping”, is considered by the specification to be a local +user-interface issue distinct from IDNA conversion functionality. + +This library provides one such mapping that was developed by the +Unicode Consortium. Known as `Unicode IDNA Compatibility Processing +`_, it provides for both a regular +mapping for typical applications, as well as a transitional mapping to +help migrate from older IDNA 2003 applications. Strings are +preprocessed according to Section 4.4 “Preprocessing for IDNA2008” +prior to the IDNA operations. + +For example, “Königsgäßchen” is not a permissible label as *LATIN +CAPITAL LETTER K* is not allowed (nor are capital letters in general). +UTS 46 will convert this into lower case prior to applying the IDNA +conversion. + +.. code-block:: pycon + + >>> import idna + >>> idna.encode('Königsgäßchen') + ... + idna.core.InvalidCodepoint: Codepoint U+004B at position 1 of 'Königsgäßchen' not allowed + >>> idna.encode('Königsgäßchen', uts46=True) + b'xn--knigsgchen-b4a3dun' + >>> print(idna.decode('xn--knigsgchen-b4a3dun')) + königsgäßchen + +Transitional processing provides conversions to help transition from +the older 2003 standard to the current standard. For example, in the +original IDNA specification, the *LATIN SMALL LETTER SHARP S* (ß) was +converted into two *LATIN SMALL LETTER S* (ss), whereas in the current +IDNA specification this conversion is not performed. + +.. code-block:: pycon + + >>> idna.encode('Königsgäßchen', uts46=True, transitional=True) + 'xn--knigsgsschen-lcb0w' + +Implementers should use transitional processing with caution, only in +rare cases where conversion from legacy labels to current labels must be +performed (i.e. IDNA implementations that pre-date 2008). For typical +applications that just need to convert labels, transitional processing +is unlikely to be beneficial and could produce unexpected incompatible +results. + +``encodings.idna`` Compatibility +++++++++++++++++++++++++++++++++ + +Function calls from the Python built-in ``encodings.idna`` module are +mapped to their IDNA 2008 equivalents using the ``idna.compat`` module. +Simply substitute the ``import`` clause in your code to refer to the new +module name. + +Exceptions +---------- + +All errors raised during the conversion following the specification +should raise an exception derived from the ``idna.IDNAError`` base +class. + +More specific exceptions that may be generated as ``idna.IDNABidiError`` +when the error reflects an illegal combination of left-to-right and +right-to-left characters in a label; ``idna.InvalidCodepoint`` when +a specific codepoint is an illegal character in an IDN label (i.e. +INVALID); and ``idna.InvalidCodepointContext`` when the codepoint is +illegal based on its positional context (i.e. it is CONTEXTO or CONTEXTJ +but the contextual requirements are not satisfied.) + +Building and Diagnostics +------------------------ + +The IDNA and UTS 46 functionality relies upon pre-calculated lookup +tables for performance. These tables are derived from computing against +eligibility criteria in the respective standards. These tables are +computed using the command-line script ``tools/idna-data``. + +This tool will fetch relevant codepoint data from the Unicode repository +and perform the required calculations to identify eligibility. There are +three main modes: + +* ``idna-data make-libdata``. Generates ``idnadata.py`` and + ``uts46data.py``, the pre-calculated lookup tables used for IDNA and + UTS 46 conversions. Implementers who wish to track this library against + a different Unicode version may use this tool to manually generate a + different version of the ``idnadata.py`` and ``uts46data.py`` files. + +* ``idna-data make-table``. Generate a table of the IDNA disposition + (e.g. PVALID, CONTEXTJ, CONTEXTO) in the format found in Appendix + B.1 of RFC 5892 and the pre-computed tables published by `IANA + `_. + +* ``idna-data U+0061``. Prints debugging output on the various + properties associated with an individual Unicode codepoint (in this + case, U+0061), that are used to assess the IDNA and UTS 46 status of a + codepoint. This is helpful in debugging or analysis. + +The tool accepts a number of arguments, described using ``idna-data +-h``. Most notably, the ``--version`` argument allows the specification +of the version of Unicode to be used in computing the table data. For +example, ``idna-data --version 9.0.0 make-libdata`` will generate +library data against Unicode 9.0.0. + + +Additional Notes +---------------- + +* **Packages**. The latest tagged release version is published in the + `Python Package Index `_. + +* **Version support**. This library supports Python 3.6 and higher. + As this library serves as a low-level toolkit for a variety of + applications, many of which strive for broad compatibility with older + Python versions, there is no rush to remove older interpreter support. + Removing support for older versions should be well justified in that the + maintenance burden has become too high. + +* **Python 2**. Python 2 is supported by version 2.x of this library. + Use "idna<3" in your requirements file if you need this library for + a Python 2 application. Be advised that these versions are no longer + actively developed. + +* **Testing**. The library has a test suite based on each rule of the + IDNA specification, as well as tests that are provided as part of the + Unicode Technical Standard 46, `Unicode IDNA Compatibility Processing + `_. + +* **Emoji**. It is an occasional request to support emoji domains in + this library. Encoding of symbols like emoji is expressly prohibited by + the technical standard IDNA 2008 and emoji domains are broadly phased + out across the domain industry due to associated security risks. For + now, applications that need to support these non-compliant labels + may wish to consider trying the encode/decode operation in this library + first, and then falling back to using `encodings.idna`. See `the Github + project `_ for more discussion. + diff --git a/venv/lib/python3.12/site-packages/idna-3.10.dist-info/RECORD b/venv/lib/python3.12/site-packages/idna-3.10.dist-info/RECORD new file mode 100644 index 00000000..9cfce7f9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/idna-3.10.dist-info/RECORD @@ -0,0 +1,22 @@ +idna-3.10.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +idna-3.10.dist-info/LICENSE.md,sha256=pZ8LDvNjWHQQmkRhykT_enDVBpboFHZ7-vch1Mmw2w8,1541 +idna-3.10.dist-info/METADATA,sha256=URR5ZyDfQ1PCEGhkYoojqfi2Ra0tau2--lhwG4XSfjI,10158 +idna-3.10.dist-info/RECORD,, +idna-3.10.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81 +idna/__init__.py,sha256=MPqNDLZbXqGaNdXxAFhiqFPKEQXju2jNQhCey6-5eJM,868 +idna/__pycache__/__init__.cpython-312.pyc,, +idna/__pycache__/codec.cpython-312.pyc,, +idna/__pycache__/compat.cpython-312.pyc,, +idna/__pycache__/core.cpython-312.pyc,, +idna/__pycache__/idnadata.cpython-312.pyc,, +idna/__pycache__/intranges.cpython-312.pyc,, +idna/__pycache__/package_data.cpython-312.pyc,, +idna/__pycache__/uts46data.cpython-312.pyc,, +idna/codec.py,sha256=PEew3ItwzjW4hymbasnty2N2OXvNcgHB-JjrBuxHPYY,3422 +idna/compat.py,sha256=RzLy6QQCdl9784aFhb2EX9EKGCJjg0P3PilGdeXXcx8,316 +idna/core.py,sha256=YJYyAMnwiQEPjVC4-Fqu_p4CJ6yKKuDGmppBNQNQpFs,13239 +idna/idnadata.py,sha256=W30GcIGvtOWYwAjZj4ZjuouUutC6ffgNuyjJy7fZ-lo,78306 +idna/intranges.py,sha256=amUtkdhYcQG8Zr-CoMM_kVRacxkivC1WgxN1b63KKdU,1898 +idna/package_data.py,sha256=q59S3OXsc5VI8j6vSD0sGBMyk6zZ4vWFREE88yCJYKs,21 +idna/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +idna/uts46data.py,sha256=rt90K9J40gUSwppDPCrhjgi5AA6pWM65dEGRSf6rIhM,239289 diff --git a/venv/lib/python3.12/site-packages/idna-3.10.dist-info/WHEEL b/venv/lib/python3.12/site-packages/idna-3.10.dist-info/WHEEL new file mode 100644 index 00000000..3b5e64b5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/idna-3.10.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: flit 3.9.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/venv/lib/python3.12/site-packages/idna/__init__.py b/venv/lib/python3.12/site-packages/idna/__init__.py new file mode 100644 index 00000000..cfdc030a --- /dev/null +++ b/venv/lib/python3.12/site-packages/idna/__init__.py @@ -0,0 +1,45 @@ +from .core import ( + IDNABidiError, + IDNAError, + InvalidCodepoint, + InvalidCodepointContext, + alabel, + check_bidi, + check_hyphen_ok, + check_initial_combiner, + check_label, + check_nfc, + decode, + encode, + ulabel, + uts46_remap, + valid_contextj, + valid_contexto, + valid_label_length, + valid_string_length, +) +from .intranges import intranges_contain +from .package_data import __version__ + +__all__ = [ + "__version__", + "IDNABidiError", + "IDNAError", + "InvalidCodepoint", + "InvalidCodepointContext", + "alabel", + "check_bidi", + "check_hyphen_ok", + "check_initial_combiner", + "check_label", + "check_nfc", + "decode", + "encode", + "intranges_contain", + "ulabel", + "uts46_remap", + "valid_contextj", + "valid_contexto", + "valid_label_length", + "valid_string_length", +] diff --git a/venv/lib/python3.12/site-packages/idna/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/idna/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 00000000..ee19d3c6 Binary files /dev/null and b/venv/lib/python3.12/site-packages/idna/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/idna/__pycache__/codec.cpython-312.pyc b/venv/lib/python3.12/site-packages/idna/__pycache__/codec.cpython-312.pyc new file mode 100644 index 00000000..7b4384e4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/idna/__pycache__/codec.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/idna/__pycache__/compat.cpython-312.pyc b/venv/lib/python3.12/site-packages/idna/__pycache__/compat.cpython-312.pyc new file mode 100644 index 00000000..004ded2f Binary files /dev/null and b/venv/lib/python3.12/site-packages/idna/__pycache__/compat.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/idna/__pycache__/core.cpython-312.pyc b/venv/lib/python3.12/site-packages/idna/__pycache__/core.cpython-312.pyc new file mode 100644 index 00000000..6777ba62 Binary files /dev/null and b/venv/lib/python3.12/site-packages/idna/__pycache__/core.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/idna/__pycache__/idnadata.cpython-312.pyc b/venv/lib/python3.12/site-packages/idna/__pycache__/idnadata.cpython-312.pyc new file mode 100644 index 00000000..ff0089e4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/idna/__pycache__/idnadata.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/idna/__pycache__/intranges.cpython-312.pyc b/venv/lib/python3.12/site-packages/idna/__pycache__/intranges.cpython-312.pyc new file mode 100644 index 00000000..5843f433 Binary files /dev/null and b/venv/lib/python3.12/site-packages/idna/__pycache__/intranges.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/idna/__pycache__/package_data.cpython-312.pyc b/venv/lib/python3.12/site-packages/idna/__pycache__/package_data.cpython-312.pyc new file mode 100644 index 00000000..ed61586e Binary files /dev/null and b/venv/lib/python3.12/site-packages/idna/__pycache__/package_data.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/idna/__pycache__/uts46data.cpython-312.pyc b/venv/lib/python3.12/site-packages/idna/__pycache__/uts46data.cpython-312.pyc new file mode 100644 index 00000000..d8dd0d74 Binary files /dev/null and b/venv/lib/python3.12/site-packages/idna/__pycache__/uts46data.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/idna/codec.py b/venv/lib/python3.12/site-packages/idna/codec.py new file mode 100644 index 00000000..913abfd6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/idna/codec.py @@ -0,0 +1,122 @@ +import codecs +import re +from typing import Any, Optional, Tuple + +from .core import IDNAError, alabel, decode, encode, ulabel + +_unicode_dots_re = re.compile("[\u002e\u3002\uff0e\uff61]") + + +class Codec(codecs.Codec): + def encode(self, data: str, errors: str = "strict") -> Tuple[bytes, int]: + if errors != "strict": + raise IDNAError('Unsupported error handling "{}"'.format(errors)) + + if not data: + return b"", 0 + + return encode(data), len(data) + + def decode(self, data: bytes, errors: str = "strict") -> Tuple[str, int]: + if errors != "strict": + raise IDNAError('Unsupported error handling "{}"'.format(errors)) + + if not data: + return "", 0 + + return decode(data), len(data) + + +class IncrementalEncoder(codecs.BufferedIncrementalEncoder): + def _buffer_encode(self, data: str, errors: str, final: bool) -> Tuple[bytes, int]: + if errors != "strict": + raise IDNAError('Unsupported error handling "{}"'.format(errors)) + + if not data: + return b"", 0 + + labels = _unicode_dots_re.split(data) + trailing_dot = b"" + if labels: + if not labels[-1]: + trailing_dot = b"." + del labels[-1] + elif not final: + # Keep potentially unfinished label until the next call + del labels[-1] + if labels: + trailing_dot = b"." + + result = [] + size = 0 + for label in labels: + result.append(alabel(label)) + if size: + size += 1 + size += len(label) + + # Join with U+002E + result_bytes = b".".join(result) + trailing_dot + size += len(trailing_dot) + return result_bytes, size + + +class IncrementalDecoder(codecs.BufferedIncrementalDecoder): + def _buffer_decode(self, data: Any, errors: str, final: bool) -> Tuple[str, int]: + if errors != "strict": + raise IDNAError('Unsupported error handling "{}"'.format(errors)) + + if not data: + return ("", 0) + + if not isinstance(data, str): + data = str(data, "ascii") + + labels = _unicode_dots_re.split(data) + trailing_dot = "" + if labels: + if not labels[-1]: + trailing_dot = "." + del labels[-1] + elif not final: + # Keep potentially unfinished label until the next call + del labels[-1] + if labels: + trailing_dot = "." + + result = [] + size = 0 + for label in labels: + result.append(ulabel(label)) + if size: + size += 1 + size += len(label) + + result_str = ".".join(result) + trailing_dot + size += len(trailing_dot) + return (result_str, size) + + +class StreamWriter(Codec, codecs.StreamWriter): + pass + + +class StreamReader(Codec, codecs.StreamReader): + pass + + +def search_function(name: str) -> Optional[codecs.CodecInfo]: + if name != "idna2008": + return None + return codecs.CodecInfo( + name=name, + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamwriter=StreamWriter, + streamreader=StreamReader, + ) + + +codecs.register(search_function) diff --git a/venv/lib/python3.12/site-packages/idna/compat.py b/venv/lib/python3.12/site-packages/idna/compat.py new file mode 100644 index 00000000..1df9f2a7 --- /dev/null +++ b/venv/lib/python3.12/site-packages/idna/compat.py @@ -0,0 +1,15 @@ +from typing import Any, Union + +from .core import decode, encode + + +def ToASCII(label: str) -> bytes: + return encode(label) + + +def ToUnicode(label: Union[bytes, bytearray]) -> str: + return decode(label) + + +def nameprep(s: Any) -> None: + raise NotImplementedError("IDNA 2008 does not utilise nameprep protocol") diff --git a/venv/lib/python3.12/site-packages/idna/core.py b/venv/lib/python3.12/site-packages/idna/core.py new file mode 100644 index 00000000..9115f123 --- /dev/null +++ b/venv/lib/python3.12/site-packages/idna/core.py @@ -0,0 +1,437 @@ +import bisect +import re +import unicodedata +from typing import Optional, Union + +from . import idnadata +from .intranges import intranges_contain + +_virama_combining_class = 9 +_alabel_prefix = b"xn--" +_unicode_dots_re = re.compile("[\u002e\u3002\uff0e\uff61]") + + +class IDNAError(UnicodeError): + """Base exception for all IDNA-encoding related problems""" + + pass + + +class IDNABidiError(IDNAError): + """Exception when bidirectional requirements are not satisfied""" + + pass + + +class InvalidCodepoint(IDNAError): + """Exception when a disallowed or unallocated codepoint is used""" + + pass + + +class InvalidCodepointContext(IDNAError): + """Exception when the codepoint is not valid in the context it is used""" + + pass + + +def _combining_class(cp: int) -> int: + v = unicodedata.combining(chr(cp)) + if v == 0: + if not unicodedata.name(chr(cp)): + raise ValueError("Unknown character in unicodedata") + return v + + +def _is_script(cp: str, script: str) -> bool: + return intranges_contain(ord(cp), idnadata.scripts[script]) + + +def _punycode(s: str) -> bytes: + return s.encode("punycode") + + +def _unot(s: int) -> str: + return "U+{:04X}".format(s) + + +def valid_label_length(label: Union[bytes, str]) -> bool: + if len(label) > 63: + return False + return True + + +def valid_string_length(label: Union[bytes, str], trailing_dot: bool) -> bool: + if len(label) > (254 if trailing_dot else 253): + return False + return True + + +def check_bidi(label: str, check_ltr: bool = False) -> bool: + # Bidi rules should only be applied if string contains RTL characters + bidi_label = False + for idx, cp in enumerate(label, 1): + direction = unicodedata.bidirectional(cp) + if direction == "": + # String likely comes from a newer version of Unicode + raise IDNABidiError("Unknown directionality in label {} at position {}".format(repr(label), idx)) + if direction in ["R", "AL", "AN"]: + bidi_label = True + if not bidi_label and not check_ltr: + return True + + # Bidi rule 1 + direction = unicodedata.bidirectional(label[0]) + if direction in ["R", "AL"]: + rtl = True + elif direction == "L": + rtl = False + else: + raise IDNABidiError("First codepoint in label {} must be directionality L, R or AL".format(repr(label))) + + valid_ending = False + number_type: Optional[str] = None + for idx, cp in enumerate(label, 1): + direction = unicodedata.bidirectional(cp) + + if rtl: + # Bidi rule 2 + if direction not in [ + "R", + "AL", + "AN", + "EN", + "ES", + "CS", + "ET", + "ON", + "BN", + "NSM", + ]: + raise IDNABidiError("Invalid direction for codepoint at position {} in a right-to-left label".format(idx)) + # Bidi rule 3 + if direction in ["R", "AL", "EN", "AN"]: + valid_ending = True + elif direction != "NSM": + valid_ending = False + # Bidi rule 4 + if direction in ["AN", "EN"]: + if not number_type: + number_type = direction + else: + if number_type != direction: + raise IDNABidiError("Can not mix numeral types in a right-to-left label") + else: + # Bidi rule 5 + if direction not in ["L", "EN", "ES", "CS", "ET", "ON", "BN", "NSM"]: + raise IDNABidiError("Invalid direction for codepoint at position {} in a left-to-right label".format(idx)) + # Bidi rule 6 + if direction in ["L", "EN"]: + valid_ending = True + elif direction != "NSM": + valid_ending = False + + if not valid_ending: + raise IDNABidiError("Label ends with illegal codepoint directionality") + + return True + + +def check_initial_combiner(label: str) -> bool: + if unicodedata.category(label[0])[0] == "M": + raise IDNAError("Label begins with an illegal combining character") + return True + + +def check_hyphen_ok(label: str) -> bool: + if label[2:4] == "--": + raise IDNAError("Label has disallowed hyphens in 3rd and 4th position") + if label[0] == "-" or label[-1] == "-": + raise IDNAError("Label must not start or end with a hyphen") + return True + + +def check_nfc(label: str) -> None: + if unicodedata.normalize("NFC", label) != label: + raise IDNAError("Label must be in Normalization Form C") + + +def valid_contextj(label: str, pos: int) -> bool: + cp_value = ord(label[pos]) + + if cp_value == 0x200C: + if pos > 0: + if _combining_class(ord(label[pos - 1])) == _virama_combining_class: + return True + + ok = False + for i in range(pos - 1, -1, -1): + joining_type = idnadata.joining_types.get(ord(label[i])) + if joining_type == ord("T"): + continue + elif joining_type in [ord("L"), ord("D")]: + ok = True + break + else: + break + + if not ok: + return False + + ok = False + for i in range(pos + 1, len(label)): + joining_type = idnadata.joining_types.get(ord(label[i])) + if joining_type == ord("T"): + continue + elif joining_type in [ord("R"), ord("D")]: + ok = True + break + else: + break + return ok + + if cp_value == 0x200D: + if pos > 0: + if _combining_class(ord(label[pos - 1])) == _virama_combining_class: + return True + return False + + else: + return False + + +def valid_contexto(label: str, pos: int, exception: bool = False) -> bool: + cp_value = ord(label[pos]) + + if cp_value == 0x00B7: + if 0 < pos < len(label) - 1: + if ord(label[pos - 1]) == 0x006C and ord(label[pos + 1]) == 0x006C: + return True + return False + + elif cp_value == 0x0375: + if pos < len(label) - 1 and len(label) > 1: + return _is_script(label[pos + 1], "Greek") + return False + + elif cp_value == 0x05F3 or cp_value == 0x05F4: + if pos > 0: + return _is_script(label[pos - 1], "Hebrew") + return False + + elif cp_value == 0x30FB: + for cp in label: + if cp == "\u30fb": + continue + if _is_script(cp, "Hiragana") or _is_script(cp, "Katakana") or _is_script(cp, "Han"): + return True + return False + + elif 0x660 <= cp_value <= 0x669: + for cp in label: + if 0x6F0 <= ord(cp) <= 0x06F9: + return False + return True + + elif 0x6F0 <= cp_value <= 0x6F9: + for cp in label: + if 0x660 <= ord(cp) <= 0x0669: + return False + return True + + return False + + +def check_label(label: Union[str, bytes, bytearray]) -> None: + if isinstance(label, (bytes, bytearray)): + label = label.decode("utf-8") + if len(label) == 0: + raise IDNAError("Empty Label") + + check_nfc(label) + check_hyphen_ok(label) + check_initial_combiner(label) + + for pos, cp in enumerate(label): + cp_value = ord(cp) + if intranges_contain(cp_value, idnadata.codepoint_classes["PVALID"]): + continue + elif intranges_contain(cp_value, idnadata.codepoint_classes["CONTEXTJ"]): + try: + if not valid_contextj(label, pos): + raise InvalidCodepointContext( + "Joiner {} not allowed at position {} in {}".format(_unot(cp_value), pos + 1, repr(label)) + ) + except ValueError: + raise IDNAError( + "Unknown codepoint adjacent to joiner {} at position {} in {}".format( + _unot(cp_value), pos + 1, repr(label) + ) + ) + elif intranges_contain(cp_value, idnadata.codepoint_classes["CONTEXTO"]): + if not valid_contexto(label, pos): + raise InvalidCodepointContext( + "Codepoint {} not allowed at position {} in {}".format(_unot(cp_value), pos + 1, repr(label)) + ) + else: + raise InvalidCodepoint( + "Codepoint {} at position {} of {} not allowed".format(_unot(cp_value), pos + 1, repr(label)) + ) + + check_bidi(label) + + +def alabel(label: str) -> bytes: + try: + label_bytes = label.encode("ascii") + ulabel(label_bytes) + if not valid_label_length(label_bytes): + raise IDNAError("Label too long") + return label_bytes + except UnicodeEncodeError: + pass + + check_label(label) + label_bytes = _alabel_prefix + _punycode(label) + + if not valid_label_length(label_bytes): + raise IDNAError("Label too long") + + return label_bytes + + +def ulabel(label: Union[str, bytes, bytearray]) -> str: + if not isinstance(label, (bytes, bytearray)): + try: + label_bytes = label.encode("ascii") + except UnicodeEncodeError: + check_label(label) + return label + else: + label_bytes = label + + label_bytes = label_bytes.lower() + if label_bytes.startswith(_alabel_prefix): + label_bytes = label_bytes[len(_alabel_prefix) :] + if not label_bytes: + raise IDNAError("Malformed A-label, no Punycode eligible content found") + if label_bytes.decode("ascii")[-1] == "-": + raise IDNAError("A-label must not end with a hyphen") + else: + check_label(label_bytes) + return label_bytes.decode("ascii") + + try: + label = label_bytes.decode("punycode") + except UnicodeError: + raise IDNAError("Invalid A-label") + check_label(label) + return label + + +def uts46_remap(domain: str, std3_rules: bool = True, transitional: bool = False) -> str: + """Re-map the characters in the string according to UTS46 processing.""" + from .uts46data import uts46data + + output = "" + + for pos, char in enumerate(domain): + code_point = ord(char) + try: + uts46row = uts46data[code_point if code_point < 256 else bisect.bisect_left(uts46data, (code_point, "Z")) - 1] + status = uts46row[1] + replacement: Optional[str] = None + if len(uts46row) == 3: + replacement = uts46row[2] + if ( + status == "V" + or (status == "D" and not transitional) + or (status == "3" and not std3_rules and replacement is None) + ): + output += char + elif replacement is not None and ( + status == "M" or (status == "3" and not std3_rules) or (status == "D" and transitional) + ): + output += replacement + elif status != "I": + raise IndexError() + except IndexError: + raise InvalidCodepoint( + "Codepoint {} not allowed at position {} in {}".format(_unot(code_point), pos + 1, repr(domain)) + ) + + return unicodedata.normalize("NFC", output) + + +def encode( + s: Union[str, bytes, bytearray], + strict: bool = False, + uts46: bool = False, + std3_rules: bool = False, + transitional: bool = False, +) -> bytes: + if not isinstance(s, str): + try: + s = str(s, "ascii") + except UnicodeDecodeError: + raise IDNAError("should pass a unicode string to the function rather than a byte string.") + if uts46: + s = uts46_remap(s, std3_rules, transitional) + trailing_dot = False + result = [] + if strict: + labels = s.split(".") + else: + labels = _unicode_dots_re.split(s) + if not labels or labels == [""]: + raise IDNAError("Empty domain") + if labels[-1] == "": + del labels[-1] + trailing_dot = True + for label in labels: + s = alabel(label) + if s: + result.append(s) + else: + raise IDNAError("Empty label") + if trailing_dot: + result.append(b"") + s = b".".join(result) + if not valid_string_length(s, trailing_dot): + raise IDNAError("Domain too long") + return s + + +def decode( + s: Union[str, bytes, bytearray], + strict: bool = False, + uts46: bool = False, + std3_rules: bool = False, +) -> str: + try: + if not isinstance(s, str): + s = str(s, "ascii") + except UnicodeDecodeError: + raise IDNAError("Invalid ASCII in A-label") + if uts46: + s = uts46_remap(s, std3_rules, False) + trailing_dot = False + result = [] + if not strict: + labels = _unicode_dots_re.split(s) + else: + labels = s.split(".") + if not labels or labels == [""]: + raise IDNAError("Empty domain") + if not labels[-1]: + del labels[-1] + trailing_dot = True + for label in labels: + s = ulabel(label) + if s: + result.append(s) + else: + raise IDNAError("Empty label") + if trailing_dot: + result.append("") + return ".".join(result) diff --git a/venv/lib/python3.12/site-packages/idna/idnadata.py b/venv/lib/python3.12/site-packages/idna/idnadata.py new file mode 100644 index 00000000..4be60046 --- /dev/null +++ b/venv/lib/python3.12/site-packages/idna/idnadata.py @@ -0,0 +1,4243 @@ +# This file is automatically generated by tools/idna-data + +__version__ = "15.1.0" +scripts = { + "Greek": ( + 0x37000000374, + 0x37500000378, + 0x37A0000037E, + 0x37F00000380, + 0x38400000385, + 0x38600000387, + 0x3880000038B, + 0x38C0000038D, + 0x38E000003A2, + 0x3A3000003E2, + 0x3F000000400, + 0x1D2600001D2B, + 0x1D5D00001D62, + 0x1D6600001D6B, + 0x1DBF00001DC0, + 0x1F0000001F16, + 0x1F1800001F1E, + 0x1F2000001F46, + 0x1F4800001F4E, + 0x1F5000001F58, + 0x1F5900001F5A, + 0x1F5B00001F5C, + 0x1F5D00001F5E, + 0x1F5F00001F7E, + 0x1F8000001FB5, + 0x1FB600001FC5, + 0x1FC600001FD4, + 0x1FD600001FDC, + 0x1FDD00001FF0, + 0x1FF200001FF5, + 0x1FF600001FFF, + 0x212600002127, + 0xAB650000AB66, + 0x101400001018F, + 0x101A0000101A1, + 0x1D2000001D246, + ), + "Han": ( + 0x2E8000002E9A, + 0x2E9B00002EF4, + 0x2F0000002FD6, + 0x300500003006, + 0x300700003008, + 0x30210000302A, + 0x30380000303C, + 0x340000004DC0, + 0x4E000000A000, + 0xF9000000FA6E, + 0xFA700000FADA, + 0x16FE200016FE4, + 0x16FF000016FF2, + 0x200000002A6E0, + 0x2A7000002B73A, + 0x2B7400002B81E, + 0x2B8200002CEA2, + 0x2CEB00002EBE1, + 0x2EBF00002EE5E, + 0x2F8000002FA1E, + 0x300000003134B, + 0x31350000323B0, + ), + "Hebrew": ( + 0x591000005C8, + 0x5D0000005EB, + 0x5EF000005F5, + 0xFB1D0000FB37, + 0xFB380000FB3D, + 0xFB3E0000FB3F, + 0xFB400000FB42, + 0xFB430000FB45, + 0xFB460000FB50, + ), + "Hiragana": ( + 0x304100003097, + 0x309D000030A0, + 0x1B0010001B120, + 0x1B1320001B133, + 0x1B1500001B153, + 0x1F2000001F201, + ), + "Katakana": ( + 0x30A1000030FB, + 0x30FD00003100, + 0x31F000003200, + 0x32D0000032FF, + 0x330000003358, + 0xFF660000FF70, + 0xFF710000FF9E, + 0x1AFF00001AFF4, + 0x1AFF50001AFFC, + 0x1AFFD0001AFFF, + 0x1B0000001B001, + 0x1B1200001B123, + 0x1B1550001B156, + 0x1B1640001B168, + ), +} +joining_types = { + 0xAD: 84, + 0x300: 84, + 0x301: 84, + 0x302: 84, + 0x303: 84, + 0x304: 84, + 0x305: 84, + 0x306: 84, + 0x307: 84, + 0x308: 84, + 0x309: 84, + 0x30A: 84, + 0x30B: 84, + 0x30C: 84, + 0x30D: 84, + 0x30E: 84, + 0x30F: 84, + 0x310: 84, + 0x311: 84, + 0x312: 84, + 0x313: 84, + 0x314: 84, + 0x315: 84, + 0x316: 84, + 0x317: 84, + 0x318: 84, + 0x319: 84, + 0x31A: 84, + 0x31B: 84, + 0x31C: 84, + 0x31D: 84, + 0x31E: 84, + 0x31F: 84, + 0x320: 84, + 0x321: 84, + 0x322: 84, + 0x323: 84, + 0x324: 84, + 0x325: 84, + 0x326: 84, + 0x327: 84, + 0x328: 84, + 0x329: 84, + 0x32A: 84, + 0x32B: 84, + 0x32C: 84, + 0x32D: 84, + 0x32E: 84, + 0x32F: 84, + 0x330: 84, + 0x331: 84, + 0x332: 84, + 0x333: 84, + 0x334: 84, + 0x335: 84, + 0x336: 84, + 0x337: 84, + 0x338: 84, + 0x339: 84, + 0x33A: 84, + 0x33B: 84, + 0x33C: 84, + 0x33D: 84, + 0x33E: 84, + 0x33F: 84, + 0x340: 84, + 0x341: 84, + 0x342: 84, + 0x343: 84, + 0x344: 84, + 0x345: 84, + 0x346: 84, + 0x347: 84, + 0x348: 84, + 0x349: 84, + 0x34A: 84, + 0x34B: 84, + 0x34C: 84, + 0x34D: 84, + 0x34E: 84, + 0x34F: 84, + 0x350: 84, + 0x351: 84, + 0x352: 84, + 0x353: 84, + 0x354: 84, + 0x355: 84, + 0x356: 84, + 0x357: 84, + 0x358: 84, + 0x359: 84, + 0x35A: 84, + 0x35B: 84, + 0x35C: 84, + 0x35D: 84, + 0x35E: 84, + 0x35F: 84, + 0x360: 84, + 0x361: 84, + 0x362: 84, + 0x363: 84, + 0x364: 84, + 0x365: 84, + 0x366: 84, + 0x367: 84, + 0x368: 84, + 0x369: 84, + 0x36A: 84, + 0x36B: 84, + 0x36C: 84, + 0x36D: 84, + 0x36E: 84, + 0x36F: 84, + 0x483: 84, + 0x484: 84, + 0x485: 84, + 0x486: 84, + 0x487: 84, + 0x488: 84, + 0x489: 84, + 0x591: 84, + 0x592: 84, + 0x593: 84, + 0x594: 84, + 0x595: 84, + 0x596: 84, + 0x597: 84, + 0x598: 84, + 0x599: 84, + 0x59A: 84, + 0x59B: 84, + 0x59C: 84, + 0x59D: 84, + 0x59E: 84, + 0x59F: 84, + 0x5A0: 84, + 0x5A1: 84, + 0x5A2: 84, + 0x5A3: 84, + 0x5A4: 84, + 0x5A5: 84, + 0x5A6: 84, + 0x5A7: 84, + 0x5A8: 84, + 0x5A9: 84, + 0x5AA: 84, + 0x5AB: 84, + 0x5AC: 84, + 0x5AD: 84, + 0x5AE: 84, + 0x5AF: 84, + 0x5B0: 84, + 0x5B1: 84, + 0x5B2: 84, + 0x5B3: 84, + 0x5B4: 84, + 0x5B5: 84, + 0x5B6: 84, + 0x5B7: 84, + 0x5B8: 84, + 0x5B9: 84, + 0x5BA: 84, + 0x5BB: 84, + 0x5BC: 84, + 0x5BD: 84, + 0x5BF: 84, + 0x5C1: 84, + 0x5C2: 84, + 0x5C4: 84, + 0x5C5: 84, + 0x5C7: 84, + 0x610: 84, + 0x611: 84, + 0x612: 84, + 0x613: 84, + 0x614: 84, + 0x615: 84, + 0x616: 84, + 0x617: 84, + 0x618: 84, + 0x619: 84, + 0x61A: 84, + 0x61C: 84, + 0x620: 68, + 0x622: 82, + 0x623: 82, + 0x624: 82, + 0x625: 82, + 0x626: 68, + 0x627: 82, + 0x628: 68, + 0x629: 82, + 0x62A: 68, + 0x62B: 68, + 0x62C: 68, + 0x62D: 68, + 0x62E: 68, + 0x62F: 82, + 0x630: 82, + 0x631: 82, + 0x632: 82, + 0x633: 68, + 0x634: 68, + 0x635: 68, + 0x636: 68, + 0x637: 68, + 0x638: 68, + 0x639: 68, + 0x63A: 68, + 0x63B: 68, + 0x63C: 68, + 0x63D: 68, + 0x63E: 68, + 0x63F: 68, + 0x640: 67, + 0x641: 68, + 0x642: 68, + 0x643: 68, + 0x644: 68, + 0x645: 68, + 0x646: 68, + 0x647: 68, + 0x648: 82, + 0x649: 68, + 0x64A: 68, + 0x64B: 84, + 0x64C: 84, + 0x64D: 84, + 0x64E: 84, + 0x64F: 84, + 0x650: 84, + 0x651: 84, + 0x652: 84, + 0x653: 84, + 0x654: 84, + 0x655: 84, + 0x656: 84, + 0x657: 84, + 0x658: 84, + 0x659: 84, + 0x65A: 84, + 0x65B: 84, + 0x65C: 84, + 0x65D: 84, + 0x65E: 84, + 0x65F: 84, + 0x66E: 68, + 0x66F: 68, + 0x670: 84, + 0x671: 82, + 0x672: 82, + 0x673: 82, + 0x675: 82, + 0x676: 82, + 0x677: 82, + 0x678: 68, + 0x679: 68, + 0x67A: 68, + 0x67B: 68, + 0x67C: 68, + 0x67D: 68, + 0x67E: 68, + 0x67F: 68, + 0x680: 68, + 0x681: 68, + 0x682: 68, + 0x683: 68, + 0x684: 68, + 0x685: 68, + 0x686: 68, + 0x687: 68, + 0x688: 82, + 0x689: 82, + 0x68A: 82, + 0x68B: 82, + 0x68C: 82, + 0x68D: 82, + 0x68E: 82, + 0x68F: 82, + 0x690: 82, + 0x691: 82, + 0x692: 82, + 0x693: 82, + 0x694: 82, + 0x695: 82, + 0x696: 82, + 0x697: 82, + 0x698: 82, + 0x699: 82, + 0x69A: 68, + 0x69B: 68, + 0x69C: 68, + 0x69D: 68, + 0x69E: 68, + 0x69F: 68, + 0x6A0: 68, + 0x6A1: 68, + 0x6A2: 68, + 0x6A3: 68, + 0x6A4: 68, + 0x6A5: 68, + 0x6A6: 68, + 0x6A7: 68, + 0x6A8: 68, + 0x6A9: 68, + 0x6AA: 68, + 0x6AB: 68, + 0x6AC: 68, + 0x6AD: 68, + 0x6AE: 68, + 0x6AF: 68, + 0x6B0: 68, + 0x6B1: 68, + 0x6B2: 68, + 0x6B3: 68, + 0x6B4: 68, + 0x6B5: 68, + 0x6B6: 68, + 0x6B7: 68, + 0x6B8: 68, + 0x6B9: 68, + 0x6BA: 68, + 0x6BB: 68, + 0x6BC: 68, + 0x6BD: 68, + 0x6BE: 68, + 0x6BF: 68, + 0x6C0: 82, + 0x6C1: 68, + 0x6C2: 68, + 0x6C3: 82, + 0x6C4: 82, + 0x6C5: 82, + 0x6C6: 82, + 0x6C7: 82, + 0x6C8: 82, + 0x6C9: 82, + 0x6CA: 82, + 0x6CB: 82, + 0x6CC: 68, + 0x6CD: 82, + 0x6CE: 68, + 0x6CF: 82, + 0x6D0: 68, + 0x6D1: 68, + 0x6D2: 82, + 0x6D3: 82, + 0x6D5: 82, + 0x6D6: 84, + 0x6D7: 84, + 0x6D8: 84, + 0x6D9: 84, + 0x6DA: 84, + 0x6DB: 84, + 0x6DC: 84, + 0x6DF: 84, + 0x6E0: 84, + 0x6E1: 84, + 0x6E2: 84, + 0x6E3: 84, + 0x6E4: 84, + 0x6E7: 84, + 0x6E8: 84, + 0x6EA: 84, + 0x6EB: 84, + 0x6EC: 84, + 0x6ED: 84, + 0x6EE: 82, + 0x6EF: 82, + 0x6FA: 68, + 0x6FB: 68, + 0x6FC: 68, + 0x6FF: 68, + 0x70F: 84, + 0x710: 82, + 0x711: 84, + 0x712: 68, + 0x713: 68, + 0x714: 68, + 0x715: 82, + 0x716: 82, + 0x717: 82, + 0x718: 82, + 0x719: 82, + 0x71A: 68, + 0x71B: 68, + 0x71C: 68, + 0x71D: 68, + 0x71E: 82, + 0x71F: 68, + 0x720: 68, + 0x721: 68, + 0x722: 68, + 0x723: 68, + 0x724: 68, + 0x725: 68, + 0x726: 68, + 0x727: 68, + 0x728: 82, + 0x729: 68, + 0x72A: 82, + 0x72B: 68, + 0x72C: 82, + 0x72D: 68, + 0x72E: 68, + 0x72F: 82, + 0x730: 84, + 0x731: 84, + 0x732: 84, + 0x733: 84, + 0x734: 84, + 0x735: 84, + 0x736: 84, + 0x737: 84, + 0x738: 84, + 0x739: 84, + 0x73A: 84, + 0x73B: 84, + 0x73C: 84, + 0x73D: 84, + 0x73E: 84, + 0x73F: 84, + 0x740: 84, + 0x741: 84, + 0x742: 84, + 0x743: 84, + 0x744: 84, + 0x745: 84, + 0x746: 84, + 0x747: 84, + 0x748: 84, + 0x749: 84, + 0x74A: 84, + 0x74D: 82, + 0x74E: 68, + 0x74F: 68, + 0x750: 68, + 0x751: 68, + 0x752: 68, + 0x753: 68, + 0x754: 68, + 0x755: 68, + 0x756: 68, + 0x757: 68, + 0x758: 68, + 0x759: 82, + 0x75A: 82, + 0x75B: 82, + 0x75C: 68, + 0x75D: 68, + 0x75E: 68, + 0x75F: 68, + 0x760: 68, + 0x761: 68, + 0x762: 68, + 0x763: 68, + 0x764: 68, + 0x765: 68, + 0x766: 68, + 0x767: 68, + 0x768: 68, + 0x769: 68, + 0x76A: 68, + 0x76B: 82, + 0x76C: 82, + 0x76D: 68, + 0x76E: 68, + 0x76F: 68, + 0x770: 68, + 0x771: 82, + 0x772: 68, + 0x773: 82, + 0x774: 82, + 0x775: 68, + 0x776: 68, + 0x777: 68, + 0x778: 82, + 0x779: 82, + 0x77A: 68, + 0x77B: 68, + 0x77C: 68, + 0x77D: 68, + 0x77E: 68, + 0x77F: 68, + 0x7A6: 84, + 0x7A7: 84, + 0x7A8: 84, + 0x7A9: 84, + 0x7AA: 84, + 0x7AB: 84, + 0x7AC: 84, + 0x7AD: 84, + 0x7AE: 84, + 0x7AF: 84, + 0x7B0: 84, + 0x7CA: 68, + 0x7CB: 68, + 0x7CC: 68, + 0x7CD: 68, + 0x7CE: 68, + 0x7CF: 68, + 0x7D0: 68, + 0x7D1: 68, + 0x7D2: 68, + 0x7D3: 68, + 0x7D4: 68, + 0x7D5: 68, + 0x7D6: 68, + 0x7D7: 68, + 0x7D8: 68, + 0x7D9: 68, + 0x7DA: 68, + 0x7DB: 68, + 0x7DC: 68, + 0x7DD: 68, + 0x7DE: 68, + 0x7DF: 68, + 0x7E0: 68, + 0x7E1: 68, + 0x7E2: 68, + 0x7E3: 68, + 0x7E4: 68, + 0x7E5: 68, + 0x7E6: 68, + 0x7E7: 68, + 0x7E8: 68, + 0x7E9: 68, + 0x7EA: 68, + 0x7EB: 84, + 0x7EC: 84, + 0x7ED: 84, + 0x7EE: 84, + 0x7EF: 84, + 0x7F0: 84, + 0x7F1: 84, + 0x7F2: 84, + 0x7F3: 84, + 0x7FA: 67, + 0x7FD: 84, + 0x816: 84, + 0x817: 84, + 0x818: 84, + 0x819: 84, + 0x81B: 84, + 0x81C: 84, + 0x81D: 84, + 0x81E: 84, + 0x81F: 84, + 0x820: 84, + 0x821: 84, + 0x822: 84, + 0x823: 84, + 0x825: 84, + 0x826: 84, + 0x827: 84, + 0x829: 84, + 0x82A: 84, + 0x82B: 84, + 0x82C: 84, + 0x82D: 84, + 0x840: 82, + 0x841: 68, + 0x842: 68, + 0x843: 68, + 0x844: 68, + 0x845: 68, + 0x846: 82, + 0x847: 82, + 0x848: 68, + 0x849: 82, + 0x84A: 68, + 0x84B: 68, + 0x84C: 68, + 0x84D: 68, + 0x84E: 68, + 0x84F: 68, + 0x850: 68, + 0x851: 68, + 0x852: 68, + 0x853: 68, + 0x854: 82, + 0x855: 68, + 0x856: 82, + 0x857: 82, + 0x858: 82, + 0x859: 84, + 0x85A: 84, + 0x85B: 84, + 0x860: 68, + 0x862: 68, + 0x863: 68, + 0x864: 68, + 0x865: 68, + 0x867: 82, + 0x868: 68, + 0x869: 82, + 0x86A: 82, + 0x870: 82, + 0x871: 82, + 0x872: 82, + 0x873: 82, + 0x874: 82, + 0x875: 82, + 0x876: 82, + 0x877: 82, + 0x878: 82, + 0x879: 82, + 0x87A: 82, + 0x87B: 82, + 0x87C: 82, + 0x87D: 82, + 0x87E: 82, + 0x87F: 82, + 0x880: 82, + 0x881: 82, + 0x882: 82, + 0x883: 67, + 0x884: 67, + 0x885: 67, + 0x886: 68, + 0x889: 68, + 0x88A: 68, + 0x88B: 68, + 0x88C: 68, + 0x88D: 68, + 0x88E: 82, + 0x898: 84, + 0x899: 84, + 0x89A: 84, + 0x89B: 84, + 0x89C: 84, + 0x89D: 84, + 0x89E: 84, + 0x89F: 84, + 0x8A0: 68, + 0x8A1: 68, + 0x8A2: 68, + 0x8A3: 68, + 0x8A4: 68, + 0x8A5: 68, + 0x8A6: 68, + 0x8A7: 68, + 0x8A8: 68, + 0x8A9: 68, + 0x8AA: 82, + 0x8AB: 82, + 0x8AC: 82, + 0x8AE: 82, + 0x8AF: 68, + 0x8B0: 68, + 0x8B1: 82, + 0x8B2: 82, + 0x8B3: 68, + 0x8B4: 68, + 0x8B5: 68, + 0x8B6: 68, + 0x8B7: 68, + 0x8B8: 68, + 0x8B9: 82, + 0x8BA: 68, + 0x8BB: 68, + 0x8BC: 68, + 0x8BD: 68, + 0x8BE: 68, + 0x8BF: 68, + 0x8C0: 68, + 0x8C1: 68, + 0x8C2: 68, + 0x8C3: 68, + 0x8C4: 68, + 0x8C5: 68, + 0x8C6: 68, + 0x8C7: 68, + 0x8C8: 68, + 0x8CA: 84, + 0x8CB: 84, + 0x8CC: 84, + 0x8CD: 84, + 0x8CE: 84, + 0x8CF: 84, + 0x8D0: 84, + 0x8D1: 84, + 0x8D2: 84, + 0x8D3: 84, + 0x8D4: 84, + 0x8D5: 84, + 0x8D6: 84, + 0x8D7: 84, + 0x8D8: 84, + 0x8D9: 84, + 0x8DA: 84, + 0x8DB: 84, + 0x8DC: 84, + 0x8DD: 84, + 0x8DE: 84, + 0x8DF: 84, + 0x8E0: 84, + 0x8E1: 84, + 0x8E3: 84, + 0x8E4: 84, + 0x8E5: 84, + 0x8E6: 84, + 0x8E7: 84, + 0x8E8: 84, + 0x8E9: 84, + 0x8EA: 84, + 0x8EB: 84, + 0x8EC: 84, + 0x8ED: 84, + 0x8EE: 84, + 0x8EF: 84, + 0x8F0: 84, + 0x8F1: 84, + 0x8F2: 84, + 0x8F3: 84, + 0x8F4: 84, + 0x8F5: 84, + 0x8F6: 84, + 0x8F7: 84, + 0x8F8: 84, + 0x8F9: 84, + 0x8FA: 84, + 0x8FB: 84, + 0x8FC: 84, + 0x8FD: 84, + 0x8FE: 84, + 0x8FF: 84, + 0x900: 84, + 0x901: 84, + 0x902: 84, + 0x93A: 84, + 0x93C: 84, + 0x941: 84, + 0x942: 84, + 0x943: 84, + 0x944: 84, + 0x945: 84, + 0x946: 84, + 0x947: 84, + 0x948: 84, + 0x94D: 84, + 0x951: 84, + 0x952: 84, + 0x953: 84, + 0x954: 84, + 0x955: 84, + 0x956: 84, + 0x957: 84, + 0x962: 84, + 0x963: 84, + 0x981: 84, + 0x9BC: 84, + 0x9C1: 84, + 0x9C2: 84, + 0x9C3: 84, + 0x9C4: 84, + 0x9CD: 84, + 0x9E2: 84, + 0x9E3: 84, + 0x9FE: 84, + 0xA01: 84, + 0xA02: 84, + 0xA3C: 84, + 0xA41: 84, + 0xA42: 84, + 0xA47: 84, + 0xA48: 84, + 0xA4B: 84, + 0xA4C: 84, + 0xA4D: 84, + 0xA51: 84, + 0xA70: 84, + 0xA71: 84, + 0xA75: 84, + 0xA81: 84, + 0xA82: 84, + 0xABC: 84, + 0xAC1: 84, + 0xAC2: 84, + 0xAC3: 84, + 0xAC4: 84, + 0xAC5: 84, + 0xAC7: 84, + 0xAC8: 84, + 0xACD: 84, + 0xAE2: 84, + 0xAE3: 84, + 0xAFA: 84, + 0xAFB: 84, + 0xAFC: 84, + 0xAFD: 84, + 0xAFE: 84, + 0xAFF: 84, + 0xB01: 84, + 0xB3C: 84, + 0xB3F: 84, + 0xB41: 84, + 0xB42: 84, + 0xB43: 84, + 0xB44: 84, + 0xB4D: 84, + 0xB55: 84, + 0xB56: 84, + 0xB62: 84, + 0xB63: 84, + 0xB82: 84, + 0xBC0: 84, + 0xBCD: 84, + 0xC00: 84, + 0xC04: 84, + 0xC3C: 84, + 0xC3E: 84, + 0xC3F: 84, + 0xC40: 84, + 0xC46: 84, + 0xC47: 84, + 0xC48: 84, + 0xC4A: 84, + 0xC4B: 84, + 0xC4C: 84, + 0xC4D: 84, + 0xC55: 84, + 0xC56: 84, + 0xC62: 84, + 0xC63: 84, + 0xC81: 84, + 0xCBC: 84, + 0xCBF: 84, + 0xCC6: 84, + 0xCCC: 84, + 0xCCD: 84, + 0xCE2: 84, + 0xCE3: 84, + 0xD00: 84, + 0xD01: 84, + 0xD3B: 84, + 0xD3C: 84, + 0xD41: 84, + 0xD42: 84, + 0xD43: 84, + 0xD44: 84, + 0xD4D: 84, + 0xD62: 84, + 0xD63: 84, + 0xD81: 84, + 0xDCA: 84, + 0xDD2: 84, + 0xDD3: 84, + 0xDD4: 84, + 0xDD6: 84, + 0xE31: 84, + 0xE34: 84, + 0xE35: 84, + 0xE36: 84, + 0xE37: 84, + 0xE38: 84, + 0xE39: 84, + 0xE3A: 84, + 0xE47: 84, + 0xE48: 84, + 0xE49: 84, + 0xE4A: 84, + 0xE4B: 84, + 0xE4C: 84, + 0xE4D: 84, + 0xE4E: 84, + 0xEB1: 84, + 0xEB4: 84, + 0xEB5: 84, + 0xEB6: 84, + 0xEB7: 84, + 0xEB8: 84, + 0xEB9: 84, + 0xEBA: 84, + 0xEBB: 84, + 0xEBC: 84, + 0xEC8: 84, + 0xEC9: 84, + 0xECA: 84, + 0xECB: 84, + 0xECC: 84, + 0xECD: 84, + 0xECE: 84, + 0xF18: 84, + 0xF19: 84, + 0xF35: 84, + 0xF37: 84, + 0xF39: 84, + 0xF71: 84, + 0xF72: 84, + 0xF73: 84, + 0xF74: 84, + 0xF75: 84, + 0xF76: 84, + 0xF77: 84, + 0xF78: 84, + 0xF79: 84, + 0xF7A: 84, + 0xF7B: 84, + 0xF7C: 84, + 0xF7D: 84, + 0xF7E: 84, + 0xF80: 84, + 0xF81: 84, + 0xF82: 84, + 0xF83: 84, + 0xF84: 84, + 0xF86: 84, + 0xF87: 84, + 0xF8D: 84, + 0xF8E: 84, + 0xF8F: 84, + 0xF90: 84, + 0xF91: 84, + 0xF92: 84, + 0xF93: 84, + 0xF94: 84, + 0xF95: 84, + 0xF96: 84, + 0xF97: 84, + 0xF99: 84, + 0xF9A: 84, + 0xF9B: 84, + 0xF9C: 84, + 0xF9D: 84, + 0xF9E: 84, + 0xF9F: 84, + 0xFA0: 84, + 0xFA1: 84, + 0xFA2: 84, + 0xFA3: 84, + 0xFA4: 84, + 0xFA5: 84, + 0xFA6: 84, + 0xFA7: 84, + 0xFA8: 84, + 0xFA9: 84, + 0xFAA: 84, + 0xFAB: 84, + 0xFAC: 84, + 0xFAD: 84, + 0xFAE: 84, + 0xFAF: 84, + 0xFB0: 84, + 0xFB1: 84, + 0xFB2: 84, + 0xFB3: 84, + 0xFB4: 84, + 0xFB5: 84, + 0xFB6: 84, + 0xFB7: 84, + 0xFB8: 84, + 0xFB9: 84, + 0xFBA: 84, + 0xFBB: 84, + 0xFBC: 84, + 0xFC6: 84, + 0x102D: 84, + 0x102E: 84, + 0x102F: 84, + 0x1030: 84, + 0x1032: 84, + 0x1033: 84, + 0x1034: 84, + 0x1035: 84, + 0x1036: 84, + 0x1037: 84, + 0x1039: 84, + 0x103A: 84, + 0x103D: 84, + 0x103E: 84, + 0x1058: 84, + 0x1059: 84, + 0x105E: 84, + 0x105F: 84, + 0x1060: 84, + 0x1071: 84, + 0x1072: 84, + 0x1073: 84, + 0x1074: 84, + 0x1082: 84, + 0x1085: 84, + 0x1086: 84, + 0x108D: 84, + 0x109D: 84, + 0x135D: 84, + 0x135E: 84, + 0x135F: 84, + 0x1712: 84, + 0x1713: 84, + 0x1714: 84, + 0x1732: 84, + 0x1733: 84, + 0x1752: 84, + 0x1753: 84, + 0x1772: 84, + 0x1773: 84, + 0x17B4: 84, + 0x17B5: 84, + 0x17B7: 84, + 0x17B8: 84, + 0x17B9: 84, + 0x17BA: 84, + 0x17BB: 84, + 0x17BC: 84, + 0x17BD: 84, + 0x17C6: 84, + 0x17C9: 84, + 0x17CA: 84, + 0x17CB: 84, + 0x17CC: 84, + 0x17CD: 84, + 0x17CE: 84, + 0x17CF: 84, + 0x17D0: 84, + 0x17D1: 84, + 0x17D2: 84, + 0x17D3: 84, + 0x17DD: 84, + 0x1807: 68, + 0x180A: 67, + 0x180B: 84, + 0x180C: 84, + 0x180D: 84, + 0x180F: 84, + 0x1820: 68, + 0x1821: 68, + 0x1822: 68, + 0x1823: 68, + 0x1824: 68, + 0x1825: 68, + 0x1826: 68, + 0x1827: 68, + 0x1828: 68, + 0x1829: 68, + 0x182A: 68, + 0x182B: 68, + 0x182C: 68, + 0x182D: 68, + 0x182E: 68, + 0x182F: 68, + 0x1830: 68, + 0x1831: 68, + 0x1832: 68, + 0x1833: 68, + 0x1834: 68, + 0x1835: 68, + 0x1836: 68, + 0x1837: 68, + 0x1838: 68, + 0x1839: 68, + 0x183A: 68, + 0x183B: 68, + 0x183C: 68, + 0x183D: 68, + 0x183E: 68, + 0x183F: 68, + 0x1840: 68, + 0x1841: 68, + 0x1842: 68, + 0x1843: 68, + 0x1844: 68, + 0x1845: 68, + 0x1846: 68, + 0x1847: 68, + 0x1848: 68, + 0x1849: 68, + 0x184A: 68, + 0x184B: 68, + 0x184C: 68, + 0x184D: 68, + 0x184E: 68, + 0x184F: 68, + 0x1850: 68, + 0x1851: 68, + 0x1852: 68, + 0x1853: 68, + 0x1854: 68, + 0x1855: 68, + 0x1856: 68, + 0x1857: 68, + 0x1858: 68, + 0x1859: 68, + 0x185A: 68, + 0x185B: 68, + 0x185C: 68, + 0x185D: 68, + 0x185E: 68, + 0x185F: 68, + 0x1860: 68, + 0x1861: 68, + 0x1862: 68, + 0x1863: 68, + 0x1864: 68, + 0x1865: 68, + 0x1866: 68, + 0x1867: 68, + 0x1868: 68, + 0x1869: 68, + 0x186A: 68, + 0x186B: 68, + 0x186C: 68, + 0x186D: 68, + 0x186E: 68, + 0x186F: 68, + 0x1870: 68, + 0x1871: 68, + 0x1872: 68, + 0x1873: 68, + 0x1874: 68, + 0x1875: 68, + 0x1876: 68, + 0x1877: 68, + 0x1878: 68, + 0x1885: 84, + 0x1886: 84, + 0x1887: 68, + 0x1888: 68, + 0x1889: 68, + 0x188A: 68, + 0x188B: 68, + 0x188C: 68, + 0x188D: 68, + 0x188E: 68, + 0x188F: 68, + 0x1890: 68, + 0x1891: 68, + 0x1892: 68, + 0x1893: 68, + 0x1894: 68, + 0x1895: 68, + 0x1896: 68, + 0x1897: 68, + 0x1898: 68, + 0x1899: 68, + 0x189A: 68, + 0x189B: 68, + 0x189C: 68, + 0x189D: 68, + 0x189E: 68, + 0x189F: 68, + 0x18A0: 68, + 0x18A1: 68, + 0x18A2: 68, + 0x18A3: 68, + 0x18A4: 68, + 0x18A5: 68, + 0x18A6: 68, + 0x18A7: 68, + 0x18A8: 68, + 0x18A9: 84, + 0x18AA: 68, + 0x1920: 84, + 0x1921: 84, + 0x1922: 84, + 0x1927: 84, + 0x1928: 84, + 0x1932: 84, + 0x1939: 84, + 0x193A: 84, + 0x193B: 84, + 0x1A17: 84, + 0x1A18: 84, + 0x1A1B: 84, + 0x1A56: 84, + 0x1A58: 84, + 0x1A59: 84, + 0x1A5A: 84, + 0x1A5B: 84, + 0x1A5C: 84, + 0x1A5D: 84, + 0x1A5E: 84, + 0x1A60: 84, + 0x1A62: 84, + 0x1A65: 84, + 0x1A66: 84, + 0x1A67: 84, + 0x1A68: 84, + 0x1A69: 84, + 0x1A6A: 84, + 0x1A6B: 84, + 0x1A6C: 84, + 0x1A73: 84, + 0x1A74: 84, + 0x1A75: 84, + 0x1A76: 84, + 0x1A77: 84, + 0x1A78: 84, + 0x1A79: 84, + 0x1A7A: 84, + 0x1A7B: 84, + 0x1A7C: 84, + 0x1A7F: 84, + 0x1AB0: 84, + 0x1AB1: 84, + 0x1AB2: 84, + 0x1AB3: 84, + 0x1AB4: 84, + 0x1AB5: 84, + 0x1AB6: 84, + 0x1AB7: 84, + 0x1AB8: 84, + 0x1AB9: 84, + 0x1ABA: 84, + 0x1ABB: 84, + 0x1ABC: 84, + 0x1ABD: 84, + 0x1ABE: 84, + 0x1ABF: 84, + 0x1AC0: 84, + 0x1AC1: 84, + 0x1AC2: 84, + 0x1AC3: 84, + 0x1AC4: 84, + 0x1AC5: 84, + 0x1AC6: 84, + 0x1AC7: 84, + 0x1AC8: 84, + 0x1AC9: 84, + 0x1ACA: 84, + 0x1ACB: 84, + 0x1ACC: 84, + 0x1ACD: 84, + 0x1ACE: 84, + 0x1B00: 84, + 0x1B01: 84, + 0x1B02: 84, + 0x1B03: 84, + 0x1B34: 84, + 0x1B36: 84, + 0x1B37: 84, + 0x1B38: 84, + 0x1B39: 84, + 0x1B3A: 84, + 0x1B3C: 84, + 0x1B42: 84, + 0x1B6B: 84, + 0x1B6C: 84, + 0x1B6D: 84, + 0x1B6E: 84, + 0x1B6F: 84, + 0x1B70: 84, + 0x1B71: 84, + 0x1B72: 84, + 0x1B73: 84, + 0x1B80: 84, + 0x1B81: 84, + 0x1BA2: 84, + 0x1BA3: 84, + 0x1BA4: 84, + 0x1BA5: 84, + 0x1BA8: 84, + 0x1BA9: 84, + 0x1BAB: 84, + 0x1BAC: 84, + 0x1BAD: 84, + 0x1BE6: 84, + 0x1BE8: 84, + 0x1BE9: 84, + 0x1BED: 84, + 0x1BEF: 84, + 0x1BF0: 84, + 0x1BF1: 84, + 0x1C2C: 84, + 0x1C2D: 84, + 0x1C2E: 84, + 0x1C2F: 84, + 0x1C30: 84, + 0x1C31: 84, + 0x1C32: 84, + 0x1C33: 84, + 0x1C36: 84, + 0x1C37: 84, + 0x1CD0: 84, + 0x1CD1: 84, + 0x1CD2: 84, + 0x1CD4: 84, + 0x1CD5: 84, + 0x1CD6: 84, + 0x1CD7: 84, + 0x1CD8: 84, + 0x1CD9: 84, + 0x1CDA: 84, + 0x1CDB: 84, + 0x1CDC: 84, + 0x1CDD: 84, + 0x1CDE: 84, + 0x1CDF: 84, + 0x1CE0: 84, + 0x1CE2: 84, + 0x1CE3: 84, + 0x1CE4: 84, + 0x1CE5: 84, + 0x1CE6: 84, + 0x1CE7: 84, + 0x1CE8: 84, + 0x1CED: 84, + 0x1CF4: 84, + 0x1CF8: 84, + 0x1CF9: 84, + 0x1DC0: 84, + 0x1DC1: 84, + 0x1DC2: 84, + 0x1DC3: 84, + 0x1DC4: 84, + 0x1DC5: 84, + 0x1DC6: 84, + 0x1DC7: 84, + 0x1DC8: 84, + 0x1DC9: 84, + 0x1DCA: 84, + 0x1DCB: 84, + 0x1DCC: 84, + 0x1DCD: 84, + 0x1DCE: 84, + 0x1DCF: 84, + 0x1DD0: 84, + 0x1DD1: 84, + 0x1DD2: 84, + 0x1DD3: 84, + 0x1DD4: 84, + 0x1DD5: 84, + 0x1DD6: 84, + 0x1DD7: 84, + 0x1DD8: 84, + 0x1DD9: 84, + 0x1DDA: 84, + 0x1DDB: 84, + 0x1DDC: 84, + 0x1DDD: 84, + 0x1DDE: 84, + 0x1DDF: 84, + 0x1DE0: 84, + 0x1DE1: 84, + 0x1DE2: 84, + 0x1DE3: 84, + 0x1DE4: 84, + 0x1DE5: 84, + 0x1DE6: 84, + 0x1DE7: 84, + 0x1DE8: 84, + 0x1DE9: 84, + 0x1DEA: 84, + 0x1DEB: 84, + 0x1DEC: 84, + 0x1DED: 84, + 0x1DEE: 84, + 0x1DEF: 84, + 0x1DF0: 84, + 0x1DF1: 84, + 0x1DF2: 84, + 0x1DF3: 84, + 0x1DF4: 84, + 0x1DF5: 84, + 0x1DF6: 84, + 0x1DF7: 84, + 0x1DF8: 84, + 0x1DF9: 84, + 0x1DFA: 84, + 0x1DFB: 84, + 0x1DFC: 84, + 0x1DFD: 84, + 0x1DFE: 84, + 0x1DFF: 84, + 0x200B: 84, + 0x200D: 67, + 0x200E: 84, + 0x200F: 84, + 0x202A: 84, + 0x202B: 84, + 0x202C: 84, + 0x202D: 84, + 0x202E: 84, + 0x2060: 84, + 0x2061: 84, + 0x2062: 84, + 0x2063: 84, + 0x2064: 84, + 0x206A: 84, + 0x206B: 84, + 0x206C: 84, + 0x206D: 84, + 0x206E: 84, + 0x206F: 84, + 0x20D0: 84, + 0x20D1: 84, + 0x20D2: 84, + 0x20D3: 84, + 0x20D4: 84, + 0x20D5: 84, + 0x20D6: 84, + 0x20D7: 84, + 0x20D8: 84, + 0x20D9: 84, + 0x20DA: 84, + 0x20DB: 84, + 0x20DC: 84, + 0x20DD: 84, + 0x20DE: 84, + 0x20DF: 84, + 0x20E0: 84, + 0x20E1: 84, + 0x20E2: 84, + 0x20E3: 84, + 0x20E4: 84, + 0x20E5: 84, + 0x20E6: 84, + 0x20E7: 84, + 0x20E8: 84, + 0x20E9: 84, + 0x20EA: 84, + 0x20EB: 84, + 0x20EC: 84, + 0x20ED: 84, + 0x20EE: 84, + 0x20EF: 84, + 0x20F0: 84, + 0x2CEF: 84, + 0x2CF0: 84, + 0x2CF1: 84, + 0x2D7F: 84, + 0x2DE0: 84, + 0x2DE1: 84, + 0x2DE2: 84, + 0x2DE3: 84, + 0x2DE4: 84, + 0x2DE5: 84, + 0x2DE6: 84, + 0x2DE7: 84, + 0x2DE8: 84, + 0x2DE9: 84, + 0x2DEA: 84, + 0x2DEB: 84, + 0x2DEC: 84, + 0x2DED: 84, + 0x2DEE: 84, + 0x2DEF: 84, + 0x2DF0: 84, + 0x2DF1: 84, + 0x2DF2: 84, + 0x2DF3: 84, + 0x2DF4: 84, + 0x2DF5: 84, + 0x2DF6: 84, + 0x2DF7: 84, + 0x2DF8: 84, + 0x2DF9: 84, + 0x2DFA: 84, + 0x2DFB: 84, + 0x2DFC: 84, + 0x2DFD: 84, + 0x2DFE: 84, + 0x2DFF: 84, + 0x302A: 84, + 0x302B: 84, + 0x302C: 84, + 0x302D: 84, + 0x3099: 84, + 0x309A: 84, + 0xA66F: 84, + 0xA670: 84, + 0xA671: 84, + 0xA672: 84, + 0xA674: 84, + 0xA675: 84, + 0xA676: 84, + 0xA677: 84, + 0xA678: 84, + 0xA679: 84, + 0xA67A: 84, + 0xA67B: 84, + 0xA67C: 84, + 0xA67D: 84, + 0xA69E: 84, + 0xA69F: 84, + 0xA6F0: 84, + 0xA6F1: 84, + 0xA802: 84, + 0xA806: 84, + 0xA80B: 84, + 0xA825: 84, + 0xA826: 84, + 0xA82C: 84, + 0xA840: 68, + 0xA841: 68, + 0xA842: 68, + 0xA843: 68, + 0xA844: 68, + 0xA845: 68, + 0xA846: 68, + 0xA847: 68, + 0xA848: 68, + 0xA849: 68, + 0xA84A: 68, + 0xA84B: 68, + 0xA84C: 68, + 0xA84D: 68, + 0xA84E: 68, + 0xA84F: 68, + 0xA850: 68, + 0xA851: 68, + 0xA852: 68, + 0xA853: 68, + 0xA854: 68, + 0xA855: 68, + 0xA856: 68, + 0xA857: 68, + 0xA858: 68, + 0xA859: 68, + 0xA85A: 68, + 0xA85B: 68, + 0xA85C: 68, + 0xA85D: 68, + 0xA85E: 68, + 0xA85F: 68, + 0xA860: 68, + 0xA861: 68, + 0xA862: 68, + 0xA863: 68, + 0xA864: 68, + 0xA865: 68, + 0xA866: 68, + 0xA867: 68, + 0xA868: 68, + 0xA869: 68, + 0xA86A: 68, + 0xA86B: 68, + 0xA86C: 68, + 0xA86D: 68, + 0xA86E: 68, + 0xA86F: 68, + 0xA870: 68, + 0xA871: 68, + 0xA872: 76, + 0xA8C4: 84, + 0xA8C5: 84, + 0xA8E0: 84, + 0xA8E1: 84, + 0xA8E2: 84, + 0xA8E3: 84, + 0xA8E4: 84, + 0xA8E5: 84, + 0xA8E6: 84, + 0xA8E7: 84, + 0xA8E8: 84, + 0xA8E9: 84, + 0xA8EA: 84, + 0xA8EB: 84, + 0xA8EC: 84, + 0xA8ED: 84, + 0xA8EE: 84, + 0xA8EF: 84, + 0xA8F0: 84, + 0xA8F1: 84, + 0xA8FF: 84, + 0xA926: 84, + 0xA927: 84, + 0xA928: 84, + 0xA929: 84, + 0xA92A: 84, + 0xA92B: 84, + 0xA92C: 84, + 0xA92D: 84, + 0xA947: 84, + 0xA948: 84, + 0xA949: 84, + 0xA94A: 84, + 0xA94B: 84, + 0xA94C: 84, + 0xA94D: 84, + 0xA94E: 84, + 0xA94F: 84, + 0xA950: 84, + 0xA951: 84, + 0xA980: 84, + 0xA981: 84, + 0xA982: 84, + 0xA9B3: 84, + 0xA9B6: 84, + 0xA9B7: 84, + 0xA9B8: 84, + 0xA9B9: 84, + 0xA9BC: 84, + 0xA9BD: 84, + 0xA9E5: 84, + 0xAA29: 84, + 0xAA2A: 84, + 0xAA2B: 84, + 0xAA2C: 84, + 0xAA2D: 84, + 0xAA2E: 84, + 0xAA31: 84, + 0xAA32: 84, + 0xAA35: 84, + 0xAA36: 84, + 0xAA43: 84, + 0xAA4C: 84, + 0xAA7C: 84, + 0xAAB0: 84, + 0xAAB2: 84, + 0xAAB3: 84, + 0xAAB4: 84, + 0xAAB7: 84, + 0xAAB8: 84, + 0xAABE: 84, + 0xAABF: 84, + 0xAAC1: 84, + 0xAAEC: 84, + 0xAAED: 84, + 0xAAF6: 84, + 0xABE5: 84, + 0xABE8: 84, + 0xABED: 84, + 0xFB1E: 84, + 0xFE00: 84, + 0xFE01: 84, + 0xFE02: 84, + 0xFE03: 84, + 0xFE04: 84, + 0xFE05: 84, + 0xFE06: 84, + 0xFE07: 84, + 0xFE08: 84, + 0xFE09: 84, + 0xFE0A: 84, + 0xFE0B: 84, + 0xFE0C: 84, + 0xFE0D: 84, + 0xFE0E: 84, + 0xFE0F: 84, + 0xFE20: 84, + 0xFE21: 84, + 0xFE22: 84, + 0xFE23: 84, + 0xFE24: 84, + 0xFE25: 84, + 0xFE26: 84, + 0xFE27: 84, + 0xFE28: 84, + 0xFE29: 84, + 0xFE2A: 84, + 0xFE2B: 84, + 0xFE2C: 84, + 0xFE2D: 84, + 0xFE2E: 84, + 0xFE2F: 84, + 0xFEFF: 84, + 0xFFF9: 84, + 0xFFFA: 84, + 0xFFFB: 84, + 0x101FD: 84, + 0x102E0: 84, + 0x10376: 84, + 0x10377: 84, + 0x10378: 84, + 0x10379: 84, + 0x1037A: 84, + 0x10A01: 84, + 0x10A02: 84, + 0x10A03: 84, + 0x10A05: 84, + 0x10A06: 84, + 0x10A0C: 84, + 0x10A0D: 84, + 0x10A0E: 84, + 0x10A0F: 84, + 0x10A38: 84, + 0x10A39: 84, + 0x10A3A: 84, + 0x10A3F: 84, + 0x10AC0: 68, + 0x10AC1: 68, + 0x10AC2: 68, + 0x10AC3: 68, + 0x10AC4: 68, + 0x10AC5: 82, + 0x10AC7: 82, + 0x10AC9: 82, + 0x10ACA: 82, + 0x10ACD: 76, + 0x10ACE: 82, + 0x10ACF: 82, + 0x10AD0: 82, + 0x10AD1: 82, + 0x10AD2: 82, + 0x10AD3: 68, + 0x10AD4: 68, + 0x10AD5: 68, + 0x10AD6: 68, + 0x10AD7: 76, + 0x10AD8: 68, + 0x10AD9: 68, + 0x10ADA: 68, + 0x10ADB: 68, + 0x10ADC: 68, + 0x10ADD: 82, + 0x10ADE: 68, + 0x10ADF: 68, + 0x10AE0: 68, + 0x10AE1: 82, + 0x10AE4: 82, + 0x10AE5: 84, + 0x10AE6: 84, + 0x10AEB: 68, + 0x10AEC: 68, + 0x10AED: 68, + 0x10AEE: 68, + 0x10AEF: 82, + 0x10B80: 68, + 0x10B81: 82, + 0x10B82: 68, + 0x10B83: 82, + 0x10B84: 82, + 0x10B85: 82, + 0x10B86: 68, + 0x10B87: 68, + 0x10B88: 68, + 0x10B89: 82, + 0x10B8A: 68, + 0x10B8B: 68, + 0x10B8C: 82, + 0x10B8D: 68, + 0x10B8E: 82, + 0x10B8F: 82, + 0x10B90: 68, + 0x10B91: 82, + 0x10BA9: 82, + 0x10BAA: 82, + 0x10BAB: 82, + 0x10BAC: 82, + 0x10BAD: 68, + 0x10BAE: 68, + 0x10D00: 76, + 0x10D01: 68, + 0x10D02: 68, + 0x10D03: 68, + 0x10D04: 68, + 0x10D05: 68, + 0x10D06: 68, + 0x10D07: 68, + 0x10D08: 68, + 0x10D09: 68, + 0x10D0A: 68, + 0x10D0B: 68, + 0x10D0C: 68, + 0x10D0D: 68, + 0x10D0E: 68, + 0x10D0F: 68, + 0x10D10: 68, + 0x10D11: 68, + 0x10D12: 68, + 0x10D13: 68, + 0x10D14: 68, + 0x10D15: 68, + 0x10D16: 68, + 0x10D17: 68, + 0x10D18: 68, + 0x10D19: 68, + 0x10D1A: 68, + 0x10D1B: 68, + 0x10D1C: 68, + 0x10D1D: 68, + 0x10D1E: 68, + 0x10D1F: 68, + 0x10D20: 68, + 0x10D21: 68, + 0x10D22: 82, + 0x10D23: 68, + 0x10D24: 84, + 0x10D25: 84, + 0x10D26: 84, + 0x10D27: 84, + 0x10EAB: 84, + 0x10EAC: 84, + 0x10EFD: 84, + 0x10EFE: 84, + 0x10EFF: 84, + 0x10F30: 68, + 0x10F31: 68, + 0x10F32: 68, + 0x10F33: 82, + 0x10F34: 68, + 0x10F35: 68, + 0x10F36: 68, + 0x10F37: 68, + 0x10F38: 68, + 0x10F39: 68, + 0x10F3A: 68, + 0x10F3B: 68, + 0x10F3C: 68, + 0x10F3D: 68, + 0x10F3E: 68, + 0x10F3F: 68, + 0x10F40: 68, + 0x10F41: 68, + 0x10F42: 68, + 0x10F43: 68, + 0x10F44: 68, + 0x10F46: 84, + 0x10F47: 84, + 0x10F48: 84, + 0x10F49: 84, + 0x10F4A: 84, + 0x10F4B: 84, + 0x10F4C: 84, + 0x10F4D: 84, + 0x10F4E: 84, + 0x10F4F: 84, + 0x10F50: 84, + 0x10F51: 68, + 0x10F52: 68, + 0x10F53: 68, + 0x10F54: 82, + 0x10F70: 68, + 0x10F71: 68, + 0x10F72: 68, + 0x10F73: 68, + 0x10F74: 82, + 0x10F75: 82, + 0x10F76: 68, + 0x10F77: 68, + 0x10F78: 68, + 0x10F79: 68, + 0x10F7A: 68, + 0x10F7B: 68, + 0x10F7C: 68, + 0x10F7D: 68, + 0x10F7E: 68, + 0x10F7F: 68, + 0x10F80: 68, + 0x10F81: 68, + 0x10F82: 84, + 0x10F83: 84, + 0x10F84: 84, + 0x10F85: 84, + 0x10FB0: 68, + 0x10FB2: 68, + 0x10FB3: 68, + 0x10FB4: 82, + 0x10FB5: 82, + 0x10FB6: 82, + 0x10FB8: 68, + 0x10FB9: 82, + 0x10FBA: 82, + 0x10FBB: 68, + 0x10FBC: 68, + 0x10FBD: 82, + 0x10FBE: 68, + 0x10FBF: 68, + 0x10FC1: 68, + 0x10FC2: 82, + 0x10FC3: 82, + 0x10FC4: 68, + 0x10FC9: 82, + 0x10FCA: 68, + 0x10FCB: 76, + 0x11001: 84, + 0x11038: 84, + 0x11039: 84, + 0x1103A: 84, + 0x1103B: 84, + 0x1103C: 84, + 0x1103D: 84, + 0x1103E: 84, + 0x1103F: 84, + 0x11040: 84, + 0x11041: 84, + 0x11042: 84, + 0x11043: 84, + 0x11044: 84, + 0x11045: 84, + 0x11046: 84, + 0x11070: 84, + 0x11073: 84, + 0x11074: 84, + 0x1107F: 84, + 0x11080: 84, + 0x11081: 84, + 0x110B3: 84, + 0x110B4: 84, + 0x110B5: 84, + 0x110B6: 84, + 0x110B9: 84, + 0x110BA: 84, + 0x110C2: 84, + 0x11100: 84, + 0x11101: 84, + 0x11102: 84, + 0x11127: 84, + 0x11128: 84, + 0x11129: 84, + 0x1112A: 84, + 0x1112B: 84, + 0x1112D: 84, + 0x1112E: 84, + 0x1112F: 84, + 0x11130: 84, + 0x11131: 84, + 0x11132: 84, + 0x11133: 84, + 0x11134: 84, + 0x11173: 84, + 0x11180: 84, + 0x11181: 84, + 0x111B6: 84, + 0x111B7: 84, + 0x111B8: 84, + 0x111B9: 84, + 0x111BA: 84, + 0x111BB: 84, + 0x111BC: 84, + 0x111BD: 84, + 0x111BE: 84, + 0x111C9: 84, + 0x111CA: 84, + 0x111CB: 84, + 0x111CC: 84, + 0x111CF: 84, + 0x1122F: 84, + 0x11230: 84, + 0x11231: 84, + 0x11234: 84, + 0x11236: 84, + 0x11237: 84, + 0x1123E: 84, + 0x11241: 84, + 0x112DF: 84, + 0x112E3: 84, + 0x112E4: 84, + 0x112E5: 84, + 0x112E6: 84, + 0x112E7: 84, + 0x112E8: 84, + 0x112E9: 84, + 0x112EA: 84, + 0x11300: 84, + 0x11301: 84, + 0x1133B: 84, + 0x1133C: 84, + 0x11340: 84, + 0x11366: 84, + 0x11367: 84, + 0x11368: 84, + 0x11369: 84, + 0x1136A: 84, + 0x1136B: 84, + 0x1136C: 84, + 0x11370: 84, + 0x11371: 84, + 0x11372: 84, + 0x11373: 84, + 0x11374: 84, + 0x11438: 84, + 0x11439: 84, + 0x1143A: 84, + 0x1143B: 84, + 0x1143C: 84, + 0x1143D: 84, + 0x1143E: 84, + 0x1143F: 84, + 0x11442: 84, + 0x11443: 84, + 0x11444: 84, + 0x11446: 84, + 0x1145E: 84, + 0x114B3: 84, + 0x114B4: 84, + 0x114B5: 84, + 0x114B6: 84, + 0x114B7: 84, + 0x114B8: 84, + 0x114BA: 84, + 0x114BF: 84, + 0x114C0: 84, + 0x114C2: 84, + 0x114C3: 84, + 0x115B2: 84, + 0x115B3: 84, + 0x115B4: 84, + 0x115B5: 84, + 0x115BC: 84, + 0x115BD: 84, + 0x115BF: 84, + 0x115C0: 84, + 0x115DC: 84, + 0x115DD: 84, + 0x11633: 84, + 0x11634: 84, + 0x11635: 84, + 0x11636: 84, + 0x11637: 84, + 0x11638: 84, + 0x11639: 84, + 0x1163A: 84, + 0x1163D: 84, + 0x1163F: 84, + 0x11640: 84, + 0x116AB: 84, + 0x116AD: 84, + 0x116B0: 84, + 0x116B1: 84, + 0x116B2: 84, + 0x116B3: 84, + 0x116B4: 84, + 0x116B5: 84, + 0x116B7: 84, + 0x1171D: 84, + 0x1171E: 84, + 0x1171F: 84, + 0x11722: 84, + 0x11723: 84, + 0x11724: 84, + 0x11725: 84, + 0x11727: 84, + 0x11728: 84, + 0x11729: 84, + 0x1172A: 84, + 0x1172B: 84, + 0x1182F: 84, + 0x11830: 84, + 0x11831: 84, + 0x11832: 84, + 0x11833: 84, + 0x11834: 84, + 0x11835: 84, + 0x11836: 84, + 0x11837: 84, + 0x11839: 84, + 0x1183A: 84, + 0x1193B: 84, + 0x1193C: 84, + 0x1193E: 84, + 0x11943: 84, + 0x119D4: 84, + 0x119D5: 84, + 0x119D6: 84, + 0x119D7: 84, + 0x119DA: 84, + 0x119DB: 84, + 0x119E0: 84, + 0x11A01: 84, + 0x11A02: 84, + 0x11A03: 84, + 0x11A04: 84, + 0x11A05: 84, + 0x11A06: 84, + 0x11A07: 84, + 0x11A08: 84, + 0x11A09: 84, + 0x11A0A: 84, + 0x11A33: 84, + 0x11A34: 84, + 0x11A35: 84, + 0x11A36: 84, + 0x11A37: 84, + 0x11A38: 84, + 0x11A3B: 84, + 0x11A3C: 84, + 0x11A3D: 84, + 0x11A3E: 84, + 0x11A47: 84, + 0x11A51: 84, + 0x11A52: 84, + 0x11A53: 84, + 0x11A54: 84, + 0x11A55: 84, + 0x11A56: 84, + 0x11A59: 84, + 0x11A5A: 84, + 0x11A5B: 84, + 0x11A8A: 84, + 0x11A8B: 84, + 0x11A8C: 84, + 0x11A8D: 84, + 0x11A8E: 84, + 0x11A8F: 84, + 0x11A90: 84, + 0x11A91: 84, + 0x11A92: 84, + 0x11A93: 84, + 0x11A94: 84, + 0x11A95: 84, + 0x11A96: 84, + 0x11A98: 84, + 0x11A99: 84, + 0x11C30: 84, + 0x11C31: 84, + 0x11C32: 84, + 0x11C33: 84, + 0x11C34: 84, + 0x11C35: 84, + 0x11C36: 84, + 0x11C38: 84, + 0x11C39: 84, + 0x11C3A: 84, + 0x11C3B: 84, + 0x11C3C: 84, + 0x11C3D: 84, + 0x11C3F: 84, + 0x11C92: 84, + 0x11C93: 84, + 0x11C94: 84, + 0x11C95: 84, + 0x11C96: 84, + 0x11C97: 84, + 0x11C98: 84, + 0x11C99: 84, + 0x11C9A: 84, + 0x11C9B: 84, + 0x11C9C: 84, + 0x11C9D: 84, + 0x11C9E: 84, + 0x11C9F: 84, + 0x11CA0: 84, + 0x11CA1: 84, + 0x11CA2: 84, + 0x11CA3: 84, + 0x11CA4: 84, + 0x11CA5: 84, + 0x11CA6: 84, + 0x11CA7: 84, + 0x11CAA: 84, + 0x11CAB: 84, + 0x11CAC: 84, + 0x11CAD: 84, + 0x11CAE: 84, + 0x11CAF: 84, + 0x11CB0: 84, + 0x11CB2: 84, + 0x11CB3: 84, + 0x11CB5: 84, + 0x11CB6: 84, + 0x11D31: 84, + 0x11D32: 84, + 0x11D33: 84, + 0x11D34: 84, + 0x11D35: 84, + 0x11D36: 84, + 0x11D3A: 84, + 0x11D3C: 84, + 0x11D3D: 84, + 0x11D3F: 84, + 0x11D40: 84, + 0x11D41: 84, + 0x11D42: 84, + 0x11D43: 84, + 0x11D44: 84, + 0x11D45: 84, + 0x11D47: 84, + 0x11D90: 84, + 0x11D91: 84, + 0x11D95: 84, + 0x11D97: 84, + 0x11EF3: 84, + 0x11EF4: 84, + 0x11F00: 84, + 0x11F01: 84, + 0x11F36: 84, + 0x11F37: 84, + 0x11F38: 84, + 0x11F39: 84, + 0x11F3A: 84, + 0x11F40: 84, + 0x11F42: 84, + 0x13430: 84, + 0x13431: 84, + 0x13432: 84, + 0x13433: 84, + 0x13434: 84, + 0x13435: 84, + 0x13436: 84, + 0x13437: 84, + 0x13438: 84, + 0x13439: 84, + 0x1343A: 84, + 0x1343B: 84, + 0x1343C: 84, + 0x1343D: 84, + 0x1343E: 84, + 0x1343F: 84, + 0x13440: 84, + 0x13447: 84, + 0x13448: 84, + 0x13449: 84, + 0x1344A: 84, + 0x1344B: 84, + 0x1344C: 84, + 0x1344D: 84, + 0x1344E: 84, + 0x1344F: 84, + 0x13450: 84, + 0x13451: 84, + 0x13452: 84, + 0x13453: 84, + 0x13454: 84, + 0x13455: 84, + 0x16AF0: 84, + 0x16AF1: 84, + 0x16AF2: 84, + 0x16AF3: 84, + 0x16AF4: 84, + 0x16B30: 84, + 0x16B31: 84, + 0x16B32: 84, + 0x16B33: 84, + 0x16B34: 84, + 0x16B35: 84, + 0x16B36: 84, + 0x16F4F: 84, + 0x16F8F: 84, + 0x16F90: 84, + 0x16F91: 84, + 0x16F92: 84, + 0x16FE4: 84, + 0x1BC9D: 84, + 0x1BC9E: 84, + 0x1BCA0: 84, + 0x1BCA1: 84, + 0x1BCA2: 84, + 0x1BCA3: 84, + 0x1CF00: 84, + 0x1CF01: 84, + 0x1CF02: 84, + 0x1CF03: 84, + 0x1CF04: 84, + 0x1CF05: 84, + 0x1CF06: 84, + 0x1CF07: 84, + 0x1CF08: 84, + 0x1CF09: 84, + 0x1CF0A: 84, + 0x1CF0B: 84, + 0x1CF0C: 84, + 0x1CF0D: 84, + 0x1CF0E: 84, + 0x1CF0F: 84, + 0x1CF10: 84, + 0x1CF11: 84, + 0x1CF12: 84, + 0x1CF13: 84, + 0x1CF14: 84, + 0x1CF15: 84, + 0x1CF16: 84, + 0x1CF17: 84, + 0x1CF18: 84, + 0x1CF19: 84, + 0x1CF1A: 84, + 0x1CF1B: 84, + 0x1CF1C: 84, + 0x1CF1D: 84, + 0x1CF1E: 84, + 0x1CF1F: 84, + 0x1CF20: 84, + 0x1CF21: 84, + 0x1CF22: 84, + 0x1CF23: 84, + 0x1CF24: 84, + 0x1CF25: 84, + 0x1CF26: 84, + 0x1CF27: 84, + 0x1CF28: 84, + 0x1CF29: 84, + 0x1CF2A: 84, + 0x1CF2B: 84, + 0x1CF2C: 84, + 0x1CF2D: 84, + 0x1CF30: 84, + 0x1CF31: 84, + 0x1CF32: 84, + 0x1CF33: 84, + 0x1CF34: 84, + 0x1CF35: 84, + 0x1CF36: 84, + 0x1CF37: 84, + 0x1CF38: 84, + 0x1CF39: 84, + 0x1CF3A: 84, + 0x1CF3B: 84, + 0x1CF3C: 84, + 0x1CF3D: 84, + 0x1CF3E: 84, + 0x1CF3F: 84, + 0x1CF40: 84, + 0x1CF41: 84, + 0x1CF42: 84, + 0x1CF43: 84, + 0x1CF44: 84, + 0x1CF45: 84, + 0x1CF46: 84, + 0x1D167: 84, + 0x1D168: 84, + 0x1D169: 84, + 0x1D173: 84, + 0x1D174: 84, + 0x1D175: 84, + 0x1D176: 84, + 0x1D177: 84, + 0x1D178: 84, + 0x1D179: 84, + 0x1D17A: 84, + 0x1D17B: 84, + 0x1D17C: 84, + 0x1D17D: 84, + 0x1D17E: 84, + 0x1D17F: 84, + 0x1D180: 84, + 0x1D181: 84, + 0x1D182: 84, + 0x1D185: 84, + 0x1D186: 84, + 0x1D187: 84, + 0x1D188: 84, + 0x1D189: 84, + 0x1D18A: 84, + 0x1D18B: 84, + 0x1D1AA: 84, + 0x1D1AB: 84, + 0x1D1AC: 84, + 0x1D1AD: 84, + 0x1D242: 84, + 0x1D243: 84, + 0x1D244: 84, + 0x1DA00: 84, + 0x1DA01: 84, + 0x1DA02: 84, + 0x1DA03: 84, + 0x1DA04: 84, + 0x1DA05: 84, + 0x1DA06: 84, + 0x1DA07: 84, + 0x1DA08: 84, + 0x1DA09: 84, + 0x1DA0A: 84, + 0x1DA0B: 84, + 0x1DA0C: 84, + 0x1DA0D: 84, + 0x1DA0E: 84, + 0x1DA0F: 84, + 0x1DA10: 84, + 0x1DA11: 84, + 0x1DA12: 84, + 0x1DA13: 84, + 0x1DA14: 84, + 0x1DA15: 84, + 0x1DA16: 84, + 0x1DA17: 84, + 0x1DA18: 84, + 0x1DA19: 84, + 0x1DA1A: 84, + 0x1DA1B: 84, + 0x1DA1C: 84, + 0x1DA1D: 84, + 0x1DA1E: 84, + 0x1DA1F: 84, + 0x1DA20: 84, + 0x1DA21: 84, + 0x1DA22: 84, + 0x1DA23: 84, + 0x1DA24: 84, + 0x1DA25: 84, + 0x1DA26: 84, + 0x1DA27: 84, + 0x1DA28: 84, + 0x1DA29: 84, + 0x1DA2A: 84, + 0x1DA2B: 84, + 0x1DA2C: 84, + 0x1DA2D: 84, + 0x1DA2E: 84, + 0x1DA2F: 84, + 0x1DA30: 84, + 0x1DA31: 84, + 0x1DA32: 84, + 0x1DA33: 84, + 0x1DA34: 84, + 0x1DA35: 84, + 0x1DA36: 84, + 0x1DA3B: 84, + 0x1DA3C: 84, + 0x1DA3D: 84, + 0x1DA3E: 84, + 0x1DA3F: 84, + 0x1DA40: 84, + 0x1DA41: 84, + 0x1DA42: 84, + 0x1DA43: 84, + 0x1DA44: 84, + 0x1DA45: 84, + 0x1DA46: 84, + 0x1DA47: 84, + 0x1DA48: 84, + 0x1DA49: 84, + 0x1DA4A: 84, + 0x1DA4B: 84, + 0x1DA4C: 84, + 0x1DA4D: 84, + 0x1DA4E: 84, + 0x1DA4F: 84, + 0x1DA50: 84, + 0x1DA51: 84, + 0x1DA52: 84, + 0x1DA53: 84, + 0x1DA54: 84, + 0x1DA55: 84, + 0x1DA56: 84, + 0x1DA57: 84, + 0x1DA58: 84, + 0x1DA59: 84, + 0x1DA5A: 84, + 0x1DA5B: 84, + 0x1DA5C: 84, + 0x1DA5D: 84, + 0x1DA5E: 84, + 0x1DA5F: 84, + 0x1DA60: 84, + 0x1DA61: 84, + 0x1DA62: 84, + 0x1DA63: 84, + 0x1DA64: 84, + 0x1DA65: 84, + 0x1DA66: 84, + 0x1DA67: 84, + 0x1DA68: 84, + 0x1DA69: 84, + 0x1DA6A: 84, + 0x1DA6B: 84, + 0x1DA6C: 84, + 0x1DA75: 84, + 0x1DA84: 84, + 0x1DA9B: 84, + 0x1DA9C: 84, + 0x1DA9D: 84, + 0x1DA9E: 84, + 0x1DA9F: 84, + 0x1DAA1: 84, + 0x1DAA2: 84, + 0x1DAA3: 84, + 0x1DAA4: 84, + 0x1DAA5: 84, + 0x1DAA6: 84, + 0x1DAA7: 84, + 0x1DAA8: 84, + 0x1DAA9: 84, + 0x1DAAA: 84, + 0x1DAAB: 84, + 0x1DAAC: 84, + 0x1DAAD: 84, + 0x1DAAE: 84, + 0x1DAAF: 84, + 0x1E000: 84, + 0x1E001: 84, + 0x1E002: 84, + 0x1E003: 84, + 0x1E004: 84, + 0x1E005: 84, + 0x1E006: 84, + 0x1E008: 84, + 0x1E009: 84, + 0x1E00A: 84, + 0x1E00B: 84, + 0x1E00C: 84, + 0x1E00D: 84, + 0x1E00E: 84, + 0x1E00F: 84, + 0x1E010: 84, + 0x1E011: 84, + 0x1E012: 84, + 0x1E013: 84, + 0x1E014: 84, + 0x1E015: 84, + 0x1E016: 84, + 0x1E017: 84, + 0x1E018: 84, + 0x1E01B: 84, + 0x1E01C: 84, + 0x1E01D: 84, + 0x1E01E: 84, + 0x1E01F: 84, + 0x1E020: 84, + 0x1E021: 84, + 0x1E023: 84, + 0x1E024: 84, + 0x1E026: 84, + 0x1E027: 84, + 0x1E028: 84, + 0x1E029: 84, + 0x1E02A: 84, + 0x1E08F: 84, + 0x1E130: 84, + 0x1E131: 84, + 0x1E132: 84, + 0x1E133: 84, + 0x1E134: 84, + 0x1E135: 84, + 0x1E136: 84, + 0x1E2AE: 84, + 0x1E2EC: 84, + 0x1E2ED: 84, + 0x1E2EE: 84, + 0x1E2EF: 84, + 0x1E4EC: 84, + 0x1E4ED: 84, + 0x1E4EE: 84, + 0x1E4EF: 84, + 0x1E8D0: 84, + 0x1E8D1: 84, + 0x1E8D2: 84, + 0x1E8D3: 84, + 0x1E8D4: 84, + 0x1E8D5: 84, + 0x1E8D6: 84, + 0x1E900: 68, + 0x1E901: 68, + 0x1E902: 68, + 0x1E903: 68, + 0x1E904: 68, + 0x1E905: 68, + 0x1E906: 68, + 0x1E907: 68, + 0x1E908: 68, + 0x1E909: 68, + 0x1E90A: 68, + 0x1E90B: 68, + 0x1E90C: 68, + 0x1E90D: 68, + 0x1E90E: 68, + 0x1E90F: 68, + 0x1E910: 68, + 0x1E911: 68, + 0x1E912: 68, + 0x1E913: 68, + 0x1E914: 68, + 0x1E915: 68, + 0x1E916: 68, + 0x1E917: 68, + 0x1E918: 68, + 0x1E919: 68, + 0x1E91A: 68, + 0x1E91B: 68, + 0x1E91C: 68, + 0x1E91D: 68, + 0x1E91E: 68, + 0x1E91F: 68, + 0x1E920: 68, + 0x1E921: 68, + 0x1E922: 68, + 0x1E923: 68, + 0x1E924: 68, + 0x1E925: 68, + 0x1E926: 68, + 0x1E927: 68, + 0x1E928: 68, + 0x1E929: 68, + 0x1E92A: 68, + 0x1E92B: 68, + 0x1E92C: 68, + 0x1E92D: 68, + 0x1E92E: 68, + 0x1E92F: 68, + 0x1E930: 68, + 0x1E931: 68, + 0x1E932: 68, + 0x1E933: 68, + 0x1E934: 68, + 0x1E935: 68, + 0x1E936: 68, + 0x1E937: 68, + 0x1E938: 68, + 0x1E939: 68, + 0x1E93A: 68, + 0x1E93B: 68, + 0x1E93C: 68, + 0x1E93D: 68, + 0x1E93E: 68, + 0x1E93F: 68, + 0x1E940: 68, + 0x1E941: 68, + 0x1E942: 68, + 0x1E943: 68, + 0x1E944: 84, + 0x1E945: 84, + 0x1E946: 84, + 0x1E947: 84, + 0x1E948: 84, + 0x1E949: 84, + 0x1E94A: 84, + 0x1E94B: 84, + 0xE0001: 84, + 0xE0020: 84, + 0xE0021: 84, + 0xE0022: 84, + 0xE0023: 84, + 0xE0024: 84, + 0xE0025: 84, + 0xE0026: 84, + 0xE0027: 84, + 0xE0028: 84, + 0xE0029: 84, + 0xE002A: 84, + 0xE002B: 84, + 0xE002C: 84, + 0xE002D: 84, + 0xE002E: 84, + 0xE002F: 84, + 0xE0030: 84, + 0xE0031: 84, + 0xE0032: 84, + 0xE0033: 84, + 0xE0034: 84, + 0xE0035: 84, + 0xE0036: 84, + 0xE0037: 84, + 0xE0038: 84, + 0xE0039: 84, + 0xE003A: 84, + 0xE003B: 84, + 0xE003C: 84, + 0xE003D: 84, + 0xE003E: 84, + 0xE003F: 84, + 0xE0040: 84, + 0xE0041: 84, + 0xE0042: 84, + 0xE0043: 84, + 0xE0044: 84, + 0xE0045: 84, + 0xE0046: 84, + 0xE0047: 84, + 0xE0048: 84, + 0xE0049: 84, + 0xE004A: 84, + 0xE004B: 84, + 0xE004C: 84, + 0xE004D: 84, + 0xE004E: 84, + 0xE004F: 84, + 0xE0050: 84, + 0xE0051: 84, + 0xE0052: 84, + 0xE0053: 84, + 0xE0054: 84, + 0xE0055: 84, + 0xE0056: 84, + 0xE0057: 84, + 0xE0058: 84, + 0xE0059: 84, + 0xE005A: 84, + 0xE005B: 84, + 0xE005C: 84, + 0xE005D: 84, + 0xE005E: 84, + 0xE005F: 84, + 0xE0060: 84, + 0xE0061: 84, + 0xE0062: 84, + 0xE0063: 84, + 0xE0064: 84, + 0xE0065: 84, + 0xE0066: 84, + 0xE0067: 84, + 0xE0068: 84, + 0xE0069: 84, + 0xE006A: 84, + 0xE006B: 84, + 0xE006C: 84, + 0xE006D: 84, + 0xE006E: 84, + 0xE006F: 84, + 0xE0070: 84, + 0xE0071: 84, + 0xE0072: 84, + 0xE0073: 84, + 0xE0074: 84, + 0xE0075: 84, + 0xE0076: 84, + 0xE0077: 84, + 0xE0078: 84, + 0xE0079: 84, + 0xE007A: 84, + 0xE007B: 84, + 0xE007C: 84, + 0xE007D: 84, + 0xE007E: 84, + 0xE007F: 84, + 0xE0100: 84, + 0xE0101: 84, + 0xE0102: 84, + 0xE0103: 84, + 0xE0104: 84, + 0xE0105: 84, + 0xE0106: 84, + 0xE0107: 84, + 0xE0108: 84, + 0xE0109: 84, + 0xE010A: 84, + 0xE010B: 84, + 0xE010C: 84, + 0xE010D: 84, + 0xE010E: 84, + 0xE010F: 84, + 0xE0110: 84, + 0xE0111: 84, + 0xE0112: 84, + 0xE0113: 84, + 0xE0114: 84, + 0xE0115: 84, + 0xE0116: 84, + 0xE0117: 84, + 0xE0118: 84, + 0xE0119: 84, + 0xE011A: 84, + 0xE011B: 84, + 0xE011C: 84, + 0xE011D: 84, + 0xE011E: 84, + 0xE011F: 84, + 0xE0120: 84, + 0xE0121: 84, + 0xE0122: 84, + 0xE0123: 84, + 0xE0124: 84, + 0xE0125: 84, + 0xE0126: 84, + 0xE0127: 84, + 0xE0128: 84, + 0xE0129: 84, + 0xE012A: 84, + 0xE012B: 84, + 0xE012C: 84, + 0xE012D: 84, + 0xE012E: 84, + 0xE012F: 84, + 0xE0130: 84, + 0xE0131: 84, + 0xE0132: 84, + 0xE0133: 84, + 0xE0134: 84, + 0xE0135: 84, + 0xE0136: 84, + 0xE0137: 84, + 0xE0138: 84, + 0xE0139: 84, + 0xE013A: 84, + 0xE013B: 84, + 0xE013C: 84, + 0xE013D: 84, + 0xE013E: 84, + 0xE013F: 84, + 0xE0140: 84, + 0xE0141: 84, + 0xE0142: 84, + 0xE0143: 84, + 0xE0144: 84, + 0xE0145: 84, + 0xE0146: 84, + 0xE0147: 84, + 0xE0148: 84, + 0xE0149: 84, + 0xE014A: 84, + 0xE014B: 84, + 0xE014C: 84, + 0xE014D: 84, + 0xE014E: 84, + 0xE014F: 84, + 0xE0150: 84, + 0xE0151: 84, + 0xE0152: 84, + 0xE0153: 84, + 0xE0154: 84, + 0xE0155: 84, + 0xE0156: 84, + 0xE0157: 84, + 0xE0158: 84, + 0xE0159: 84, + 0xE015A: 84, + 0xE015B: 84, + 0xE015C: 84, + 0xE015D: 84, + 0xE015E: 84, + 0xE015F: 84, + 0xE0160: 84, + 0xE0161: 84, + 0xE0162: 84, + 0xE0163: 84, + 0xE0164: 84, + 0xE0165: 84, + 0xE0166: 84, + 0xE0167: 84, + 0xE0168: 84, + 0xE0169: 84, + 0xE016A: 84, + 0xE016B: 84, + 0xE016C: 84, + 0xE016D: 84, + 0xE016E: 84, + 0xE016F: 84, + 0xE0170: 84, + 0xE0171: 84, + 0xE0172: 84, + 0xE0173: 84, + 0xE0174: 84, + 0xE0175: 84, + 0xE0176: 84, + 0xE0177: 84, + 0xE0178: 84, + 0xE0179: 84, + 0xE017A: 84, + 0xE017B: 84, + 0xE017C: 84, + 0xE017D: 84, + 0xE017E: 84, + 0xE017F: 84, + 0xE0180: 84, + 0xE0181: 84, + 0xE0182: 84, + 0xE0183: 84, + 0xE0184: 84, + 0xE0185: 84, + 0xE0186: 84, + 0xE0187: 84, + 0xE0188: 84, + 0xE0189: 84, + 0xE018A: 84, + 0xE018B: 84, + 0xE018C: 84, + 0xE018D: 84, + 0xE018E: 84, + 0xE018F: 84, + 0xE0190: 84, + 0xE0191: 84, + 0xE0192: 84, + 0xE0193: 84, + 0xE0194: 84, + 0xE0195: 84, + 0xE0196: 84, + 0xE0197: 84, + 0xE0198: 84, + 0xE0199: 84, + 0xE019A: 84, + 0xE019B: 84, + 0xE019C: 84, + 0xE019D: 84, + 0xE019E: 84, + 0xE019F: 84, + 0xE01A0: 84, + 0xE01A1: 84, + 0xE01A2: 84, + 0xE01A3: 84, + 0xE01A4: 84, + 0xE01A5: 84, + 0xE01A6: 84, + 0xE01A7: 84, + 0xE01A8: 84, + 0xE01A9: 84, + 0xE01AA: 84, + 0xE01AB: 84, + 0xE01AC: 84, + 0xE01AD: 84, + 0xE01AE: 84, + 0xE01AF: 84, + 0xE01B0: 84, + 0xE01B1: 84, + 0xE01B2: 84, + 0xE01B3: 84, + 0xE01B4: 84, + 0xE01B5: 84, + 0xE01B6: 84, + 0xE01B7: 84, + 0xE01B8: 84, + 0xE01B9: 84, + 0xE01BA: 84, + 0xE01BB: 84, + 0xE01BC: 84, + 0xE01BD: 84, + 0xE01BE: 84, + 0xE01BF: 84, + 0xE01C0: 84, + 0xE01C1: 84, + 0xE01C2: 84, + 0xE01C3: 84, + 0xE01C4: 84, + 0xE01C5: 84, + 0xE01C6: 84, + 0xE01C7: 84, + 0xE01C8: 84, + 0xE01C9: 84, + 0xE01CA: 84, + 0xE01CB: 84, + 0xE01CC: 84, + 0xE01CD: 84, + 0xE01CE: 84, + 0xE01CF: 84, + 0xE01D0: 84, + 0xE01D1: 84, + 0xE01D2: 84, + 0xE01D3: 84, + 0xE01D4: 84, + 0xE01D5: 84, + 0xE01D6: 84, + 0xE01D7: 84, + 0xE01D8: 84, + 0xE01D9: 84, + 0xE01DA: 84, + 0xE01DB: 84, + 0xE01DC: 84, + 0xE01DD: 84, + 0xE01DE: 84, + 0xE01DF: 84, + 0xE01E0: 84, + 0xE01E1: 84, + 0xE01E2: 84, + 0xE01E3: 84, + 0xE01E4: 84, + 0xE01E5: 84, + 0xE01E6: 84, + 0xE01E7: 84, + 0xE01E8: 84, + 0xE01E9: 84, + 0xE01EA: 84, + 0xE01EB: 84, + 0xE01EC: 84, + 0xE01ED: 84, + 0xE01EE: 84, + 0xE01EF: 84, +} +codepoint_classes = { + "PVALID": ( + 0x2D0000002E, + 0x300000003A, + 0x610000007B, + 0xDF000000F7, + 0xF800000100, + 0x10100000102, + 0x10300000104, + 0x10500000106, + 0x10700000108, + 0x1090000010A, + 0x10B0000010C, + 0x10D0000010E, + 0x10F00000110, + 0x11100000112, + 0x11300000114, + 0x11500000116, + 0x11700000118, + 0x1190000011A, + 0x11B0000011C, + 0x11D0000011E, + 0x11F00000120, + 0x12100000122, + 0x12300000124, + 0x12500000126, + 0x12700000128, + 0x1290000012A, + 0x12B0000012C, + 0x12D0000012E, + 0x12F00000130, + 0x13100000132, + 0x13500000136, + 0x13700000139, + 0x13A0000013B, + 0x13C0000013D, + 0x13E0000013F, + 0x14200000143, + 0x14400000145, + 0x14600000147, + 0x14800000149, + 0x14B0000014C, + 0x14D0000014E, + 0x14F00000150, + 0x15100000152, + 0x15300000154, + 0x15500000156, + 0x15700000158, + 0x1590000015A, + 0x15B0000015C, + 0x15D0000015E, + 0x15F00000160, + 0x16100000162, + 0x16300000164, + 0x16500000166, + 0x16700000168, + 0x1690000016A, + 0x16B0000016C, + 0x16D0000016E, + 0x16F00000170, + 0x17100000172, + 0x17300000174, + 0x17500000176, + 0x17700000178, + 0x17A0000017B, + 0x17C0000017D, + 0x17E0000017F, + 0x18000000181, + 0x18300000184, + 0x18500000186, + 0x18800000189, + 0x18C0000018E, + 0x19200000193, + 0x19500000196, + 0x1990000019C, + 0x19E0000019F, + 0x1A1000001A2, + 0x1A3000001A4, + 0x1A5000001A6, + 0x1A8000001A9, + 0x1AA000001AC, + 0x1AD000001AE, + 0x1B0000001B1, + 0x1B4000001B5, + 0x1B6000001B7, + 0x1B9000001BC, + 0x1BD000001C4, + 0x1CE000001CF, + 0x1D0000001D1, + 0x1D2000001D3, + 0x1D4000001D5, + 0x1D6000001D7, + 0x1D8000001D9, + 0x1DA000001DB, + 0x1DC000001DE, + 0x1DF000001E0, + 0x1E1000001E2, + 0x1E3000001E4, + 0x1E5000001E6, + 0x1E7000001E8, + 0x1E9000001EA, + 0x1EB000001EC, + 0x1ED000001EE, + 0x1EF000001F1, + 0x1F5000001F6, + 0x1F9000001FA, + 0x1FB000001FC, + 0x1FD000001FE, + 0x1FF00000200, + 0x20100000202, + 0x20300000204, + 0x20500000206, + 0x20700000208, + 0x2090000020A, + 0x20B0000020C, + 0x20D0000020E, + 0x20F00000210, + 0x21100000212, + 0x21300000214, + 0x21500000216, + 0x21700000218, + 0x2190000021A, + 0x21B0000021C, + 0x21D0000021E, + 0x21F00000220, + 0x22100000222, + 0x22300000224, + 0x22500000226, + 0x22700000228, + 0x2290000022A, + 0x22B0000022C, + 0x22D0000022E, + 0x22F00000230, + 0x23100000232, + 0x2330000023A, + 0x23C0000023D, + 0x23F00000241, + 0x24200000243, + 0x24700000248, + 0x2490000024A, + 0x24B0000024C, + 0x24D0000024E, + 0x24F000002B0, + 0x2B9000002C2, + 0x2C6000002D2, + 0x2EC000002ED, + 0x2EE000002EF, + 0x30000000340, + 0x34200000343, + 0x3460000034F, + 0x35000000370, + 0x37100000372, + 0x37300000374, + 0x37700000378, + 0x37B0000037E, + 0x39000000391, + 0x3AC000003CF, + 0x3D7000003D8, + 0x3D9000003DA, + 0x3DB000003DC, + 0x3DD000003DE, + 0x3DF000003E0, + 0x3E1000003E2, + 0x3E3000003E4, + 0x3E5000003E6, + 0x3E7000003E8, + 0x3E9000003EA, + 0x3EB000003EC, + 0x3ED000003EE, + 0x3EF000003F0, + 0x3F3000003F4, + 0x3F8000003F9, + 0x3FB000003FD, + 0x43000000460, + 0x46100000462, + 0x46300000464, + 0x46500000466, + 0x46700000468, + 0x4690000046A, + 0x46B0000046C, + 0x46D0000046E, + 0x46F00000470, + 0x47100000472, + 0x47300000474, + 0x47500000476, + 0x47700000478, + 0x4790000047A, + 0x47B0000047C, + 0x47D0000047E, + 0x47F00000480, + 0x48100000482, + 0x48300000488, + 0x48B0000048C, + 0x48D0000048E, + 0x48F00000490, + 0x49100000492, + 0x49300000494, + 0x49500000496, + 0x49700000498, + 0x4990000049A, + 0x49B0000049C, + 0x49D0000049E, + 0x49F000004A0, + 0x4A1000004A2, + 0x4A3000004A4, + 0x4A5000004A6, + 0x4A7000004A8, + 0x4A9000004AA, + 0x4AB000004AC, + 0x4AD000004AE, + 0x4AF000004B0, + 0x4B1000004B2, + 0x4B3000004B4, + 0x4B5000004B6, + 0x4B7000004B8, + 0x4B9000004BA, + 0x4BB000004BC, + 0x4BD000004BE, + 0x4BF000004C0, + 0x4C2000004C3, + 0x4C4000004C5, + 0x4C6000004C7, + 0x4C8000004C9, + 0x4CA000004CB, + 0x4CC000004CD, + 0x4CE000004D0, + 0x4D1000004D2, + 0x4D3000004D4, + 0x4D5000004D6, + 0x4D7000004D8, + 0x4D9000004DA, + 0x4DB000004DC, + 0x4DD000004DE, + 0x4DF000004E0, + 0x4E1000004E2, + 0x4E3000004E4, + 0x4E5000004E6, + 0x4E7000004E8, + 0x4E9000004EA, + 0x4EB000004EC, + 0x4ED000004EE, + 0x4EF000004F0, + 0x4F1000004F2, + 0x4F3000004F4, + 0x4F5000004F6, + 0x4F7000004F8, + 0x4F9000004FA, + 0x4FB000004FC, + 0x4FD000004FE, + 0x4FF00000500, + 0x50100000502, + 0x50300000504, + 0x50500000506, + 0x50700000508, + 0x5090000050A, + 0x50B0000050C, + 0x50D0000050E, + 0x50F00000510, + 0x51100000512, + 0x51300000514, + 0x51500000516, + 0x51700000518, + 0x5190000051A, + 0x51B0000051C, + 0x51D0000051E, + 0x51F00000520, + 0x52100000522, + 0x52300000524, + 0x52500000526, + 0x52700000528, + 0x5290000052A, + 0x52B0000052C, + 0x52D0000052E, + 0x52F00000530, + 0x5590000055A, + 0x56000000587, + 0x58800000589, + 0x591000005BE, + 0x5BF000005C0, + 0x5C1000005C3, + 0x5C4000005C6, + 0x5C7000005C8, + 0x5D0000005EB, + 0x5EF000005F3, + 0x6100000061B, + 0x62000000640, + 0x64100000660, + 0x66E00000675, + 0x679000006D4, + 0x6D5000006DD, + 0x6DF000006E9, + 0x6EA000006F0, + 0x6FA00000700, + 0x7100000074B, + 0x74D000007B2, + 0x7C0000007F6, + 0x7FD000007FE, + 0x8000000082E, + 0x8400000085C, + 0x8600000086B, + 0x87000000888, + 0x8890000088F, + 0x898000008E2, + 0x8E300000958, + 0x96000000964, + 0x96600000970, + 0x97100000984, + 0x9850000098D, + 0x98F00000991, + 0x993000009A9, + 0x9AA000009B1, + 0x9B2000009B3, + 0x9B6000009BA, + 0x9BC000009C5, + 0x9C7000009C9, + 0x9CB000009CF, + 0x9D7000009D8, + 0x9E0000009E4, + 0x9E6000009F2, + 0x9FC000009FD, + 0x9FE000009FF, + 0xA0100000A04, + 0xA0500000A0B, + 0xA0F00000A11, + 0xA1300000A29, + 0xA2A00000A31, + 0xA3200000A33, + 0xA3500000A36, + 0xA3800000A3A, + 0xA3C00000A3D, + 0xA3E00000A43, + 0xA4700000A49, + 0xA4B00000A4E, + 0xA5100000A52, + 0xA5C00000A5D, + 0xA6600000A76, + 0xA8100000A84, + 0xA8500000A8E, + 0xA8F00000A92, + 0xA9300000AA9, + 0xAAA00000AB1, + 0xAB200000AB4, + 0xAB500000ABA, + 0xABC00000AC6, + 0xAC700000ACA, + 0xACB00000ACE, + 0xAD000000AD1, + 0xAE000000AE4, + 0xAE600000AF0, + 0xAF900000B00, + 0xB0100000B04, + 0xB0500000B0D, + 0xB0F00000B11, + 0xB1300000B29, + 0xB2A00000B31, + 0xB3200000B34, + 0xB3500000B3A, + 0xB3C00000B45, + 0xB4700000B49, + 0xB4B00000B4E, + 0xB5500000B58, + 0xB5F00000B64, + 0xB6600000B70, + 0xB7100000B72, + 0xB8200000B84, + 0xB8500000B8B, + 0xB8E00000B91, + 0xB9200000B96, + 0xB9900000B9B, + 0xB9C00000B9D, + 0xB9E00000BA0, + 0xBA300000BA5, + 0xBA800000BAB, + 0xBAE00000BBA, + 0xBBE00000BC3, + 0xBC600000BC9, + 0xBCA00000BCE, + 0xBD000000BD1, + 0xBD700000BD8, + 0xBE600000BF0, + 0xC0000000C0D, + 0xC0E00000C11, + 0xC1200000C29, + 0xC2A00000C3A, + 0xC3C00000C45, + 0xC4600000C49, + 0xC4A00000C4E, + 0xC5500000C57, + 0xC5800000C5B, + 0xC5D00000C5E, + 0xC6000000C64, + 0xC6600000C70, + 0xC8000000C84, + 0xC8500000C8D, + 0xC8E00000C91, + 0xC9200000CA9, + 0xCAA00000CB4, + 0xCB500000CBA, + 0xCBC00000CC5, + 0xCC600000CC9, + 0xCCA00000CCE, + 0xCD500000CD7, + 0xCDD00000CDF, + 0xCE000000CE4, + 0xCE600000CF0, + 0xCF100000CF4, + 0xD0000000D0D, + 0xD0E00000D11, + 0xD1200000D45, + 0xD4600000D49, + 0xD4A00000D4F, + 0xD5400000D58, + 0xD5F00000D64, + 0xD6600000D70, + 0xD7A00000D80, + 0xD8100000D84, + 0xD8500000D97, + 0xD9A00000DB2, + 0xDB300000DBC, + 0xDBD00000DBE, + 0xDC000000DC7, + 0xDCA00000DCB, + 0xDCF00000DD5, + 0xDD600000DD7, + 0xDD800000DE0, + 0xDE600000DF0, + 0xDF200000DF4, + 0xE0100000E33, + 0xE3400000E3B, + 0xE4000000E4F, + 0xE5000000E5A, + 0xE8100000E83, + 0xE8400000E85, + 0xE8600000E8B, + 0xE8C00000EA4, + 0xEA500000EA6, + 0xEA700000EB3, + 0xEB400000EBE, + 0xEC000000EC5, + 0xEC600000EC7, + 0xEC800000ECF, + 0xED000000EDA, + 0xEDE00000EE0, + 0xF0000000F01, + 0xF0B00000F0C, + 0xF1800000F1A, + 0xF2000000F2A, + 0xF3500000F36, + 0xF3700000F38, + 0xF3900000F3A, + 0xF3E00000F43, + 0xF4400000F48, + 0xF4900000F4D, + 0xF4E00000F52, + 0xF5300000F57, + 0xF5800000F5C, + 0xF5D00000F69, + 0xF6A00000F6D, + 0xF7100000F73, + 0xF7400000F75, + 0xF7A00000F81, + 0xF8200000F85, + 0xF8600000F93, + 0xF9400000F98, + 0xF9900000F9D, + 0xF9E00000FA2, + 0xFA300000FA7, + 0xFA800000FAC, + 0xFAD00000FB9, + 0xFBA00000FBD, + 0xFC600000FC7, + 0x10000000104A, + 0x10500000109E, + 0x10D0000010FB, + 0x10FD00001100, + 0x120000001249, + 0x124A0000124E, + 0x125000001257, + 0x125800001259, + 0x125A0000125E, + 0x126000001289, + 0x128A0000128E, + 0x1290000012B1, + 0x12B2000012B6, + 0x12B8000012BF, + 0x12C0000012C1, + 0x12C2000012C6, + 0x12C8000012D7, + 0x12D800001311, + 0x131200001316, + 0x13180000135B, + 0x135D00001360, + 0x138000001390, + 0x13A0000013F6, + 0x14010000166D, + 0x166F00001680, + 0x16810000169B, + 0x16A0000016EB, + 0x16F1000016F9, + 0x170000001716, + 0x171F00001735, + 0x174000001754, + 0x17600000176D, + 0x176E00001771, + 0x177200001774, + 0x1780000017B4, + 0x17B6000017D4, + 0x17D7000017D8, + 0x17DC000017DE, + 0x17E0000017EA, + 0x18100000181A, + 0x182000001879, + 0x1880000018AB, + 0x18B0000018F6, + 0x19000000191F, + 0x19200000192C, + 0x19300000193C, + 0x19460000196E, + 0x197000001975, + 0x1980000019AC, + 0x19B0000019CA, + 0x19D0000019DA, + 0x1A0000001A1C, + 0x1A2000001A5F, + 0x1A6000001A7D, + 0x1A7F00001A8A, + 0x1A9000001A9A, + 0x1AA700001AA8, + 0x1AB000001ABE, + 0x1ABF00001ACF, + 0x1B0000001B4D, + 0x1B5000001B5A, + 0x1B6B00001B74, + 0x1B8000001BF4, + 0x1C0000001C38, + 0x1C4000001C4A, + 0x1C4D00001C7E, + 0x1CD000001CD3, + 0x1CD400001CFB, + 0x1D0000001D2C, + 0x1D2F00001D30, + 0x1D3B00001D3C, + 0x1D4E00001D4F, + 0x1D6B00001D78, + 0x1D7900001D9B, + 0x1DC000001E00, + 0x1E0100001E02, + 0x1E0300001E04, + 0x1E0500001E06, + 0x1E0700001E08, + 0x1E0900001E0A, + 0x1E0B00001E0C, + 0x1E0D00001E0E, + 0x1E0F00001E10, + 0x1E1100001E12, + 0x1E1300001E14, + 0x1E1500001E16, + 0x1E1700001E18, + 0x1E1900001E1A, + 0x1E1B00001E1C, + 0x1E1D00001E1E, + 0x1E1F00001E20, + 0x1E2100001E22, + 0x1E2300001E24, + 0x1E2500001E26, + 0x1E2700001E28, + 0x1E2900001E2A, + 0x1E2B00001E2C, + 0x1E2D00001E2E, + 0x1E2F00001E30, + 0x1E3100001E32, + 0x1E3300001E34, + 0x1E3500001E36, + 0x1E3700001E38, + 0x1E3900001E3A, + 0x1E3B00001E3C, + 0x1E3D00001E3E, + 0x1E3F00001E40, + 0x1E4100001E42, + 0x1E4300001E44, + 0x1E4500001E46, + 0x1E4700001E48, + 0x1E4900001E4A, + 0x1E4B00001E4C, + 0x1E4D00001E4E, + 0x1E4F00001E50, + 0x1E5100001E52, + 0x1E5300001E54, + 0x1E5500001E56, + 0x1E5700001E58, + 0x1E5900001E5A, + 0x1E5B00001E5C, + 0x1E5D00001E5E, + 0x1E5F00001E60, + 0x1E6100001E62, + 0x1E6300001E64, + 0x1E6500001E66, + 0x1E6700001E68, + 0x1E6900001E6A, + 0x1E6B00001E6C, + 0x1E6D00001E6E, + 0x1E6F00001E70, + 0x1E7100001E72, + 0x1E7300001E74, + 0x1E7500001E76, + 0x1E7700001E78, + 0x1E7900001E7A, + 0x1E7B00001E7C, + 0x1E7D00001E7E, + 0x1E7F00001E80, + 0x1E8100001E82, + 0x1E8300001E84, + 0x1E8500001E86, + 0x1E8700001E88, + 0x1E8900001E8A, + 0x1E8B00001E8C, + 0x1E8D00001E8E, + 0x1E8F00001E90, + 0x1E9100001E92, + 0x1E9300001E94, + 0x1E9500001E9A, + 0x1E9C00001E9E, + 0x1E9F00001EA0, + 0x1EA100001EA2, + 0x1EA300001EA4, + 0x1EA500001EA6, + 0x1EA700001EA8, + 0x1EA900001EAA, + 0x1EAB00001EAC, + 0x1EAD00001EAE, + 0x1EAF00001EB0, + 0x1EB100001EB2, + 0x1EB300001EB4, + 0x1EB500001EB6, + 0x1EB700001EB8, + 0x1EB900001EBA, + 0x1EBB00001EBC, + 0x1EBD00001EBE, + 0x1EBF00001EC0, + 0x1EC100001EC2, + 0x1EC300001EC4, + 0x1EC500001EC6, + 0x1EC700001EC8, + 0x1EC900001ECA, + 0x1ECB00001ECC, + 0x1ECD00001ECE, + 0x1ECF00001ED0, + 0x1ED100001ED2, + 0x1ED300001ED4, + 0x1ED500001ED6, + 0x1ED700001ED8, + 0x1ED900001EDA, + 0x1EDB00001EDC, + 0x1EDD00001EDE, + 0x1EDF00001EE0, + 0x1EE100001EE2, + 0x1EE300001EE4, + 0x1EE500001EE6, + 0x1EE700001EE8, + 0x1EE900001EEA, + 0x1EEB00001EEC, + 0x1EED00001EEE, + 0x1EEF00001EF0, + 0x1EF100001EF2, + 0x1EF300001EF4, + 0x1EF500001EF6, + 0x1EF700001EF8, + 0x1EF900001EFA, + 0x1EFB00001EFC, + 0x1EFD00001EFE, + 0x1EFF00001F08, + 0x1F1000001F16, + 0x1F2000001F28, + 0x1F3000001F38, + 0x1F4000001F46, + 0x1F5000001F58, + 0x1F6000001F68, + 0x1F7000001F71, + 0x1F7200001F73, + 0x1F7400001F75, + 0x1F7600001F77, + 0x1F7800001F79, + 0x1F7A00001F7B, + 0x1F7C00001F7D, + 0x1FB000001FB2, + 0x1FB600001FB7, + 0x1FC600001FC7, + 0x1FD000001FD3, + 0x1FD600001FD8, + 0x1FE000001FE3, + 0x1FE400001FE8, + 0x1FF600001FF7, + 0x214E0000214F, + 0x218400002185, + 0x2C3000002C60, + 0x2C6100002C62, + 0x2C6500002C67, + 0x2C6800002C69, + 0x2C6A00002C6B, + 0x2C6C00002C6D, + 0x2C7100002C72, + 0x2C7300002C75, + 0x2C7600002C7C, + 0x2C8100002C82, + 0x2C8300002C84, + 0x2C8500002C86, + 0x2C8700002C88, + 0x2C8900002C8A, + 0x2C8B00002C8C, + 0x2C8D00002C8E, + 0x2C8F00002C90, + 0x2C9100002C92, + 0x2C9300002C94, + 0x2C9500002C96, + 0x2C9700002C98, + 0x2C9900002C9A, + 0x2C9B00002C9C, + 0x2C9D00002C9E, + 0x2C9F00002CA0, + 0x2CA100002CA2, + 0x2CA300002CA4, + 0x2CA500002CA6, + 0x2CA700002CA8, + 0x2CA900002CAA, + 0x2CAB00002CAC, + 0x2CAD00002CAE, + 0x2CAF00002CB0, + 0x2CB100002CB2, + 0x2CB300002CB4, + 0x2CB500002CB6, + 0x2CB700002CB8, + 0x2CB900002CBA, + 0x2CBB00002CBC, + 0x2CBD00002CBE, + 0x2CBF00002CC0, + 0x2CC100002CC2, + 0x2CC300002CC4, + 0x2CC500002CC6, + 0x2CC700002CC8, + 0x2CC900002CCA, + 0x2CCB00002CCC, + 0x2CCD00002CCE, + 0x2CCF00002CD0, + 0x2CD100002CD2, + 0x2CD300002CD4, + 0x2CD500002CD6, + 0x2CD700002CD8, + 0x2CD900002CDA, + 0x2CDB00002CDC, + 0x2CDD00002CDE, + 0x2CDF00002CE0, + 0x2CE100002CE2, + 0x2CE300002CE5, + 0x2CEC00002CED, + 0x2CEE00002CF2, + 0x2CF300002CF4, + 0x2D0000002D26, + 0x2D2700002D28, + 0x2D2D00002D2E, + 0x2D3000002D68, + 0x2D7F00002D97, + 0x2DA000002DA7, + 0x2DA800002DAF, + 0x2DB000002DB7, + 0x2DB800002DBF, + 0x2DC000002DC7, + 0x2DC800002DCF, + 0x2DD000002DD7, + 0x2DD800002DDF, + 0x2DE000002E00, + 0x2E2F00002E30, + 0x300500003008, + 0x302A0000302E, + 0x303C0000303D, + 0x304100003097, + 0x30990000309B, + 0x309D0000309F, + 0x30A1000030FB, + 0x30FC000030FF, + 0x310500003130, + 0x31A0000031C0, + 0x31F000003200, + 0x340000004DC0, + 0x4E000000A48D, + 0xA4D00000A4FE, + 0xA5000000A60D, + 0xA6100000A62C, + 0xA6410000A642, + 0xA6430000A644, + 0xA6450000A646, + 0xA6470000A648, + 0xA6490000A64A, + 0xA64B0000A64C, + 0xA64D0000A64E, + 0xA64F0000A650, + 0xA6510000A652, + 0xA6530000A654, + 0xA6550000A656, + 0xA6570000A658, + 0xA6590000A65A, + 0xA65B0000A65C, + 0xA65D0000A65E, + 0xA65F0000A660, + 0xA6610000A662, + 0xA6630000A664, + 0xA6650000A666, + 0xA6670000A668, + 0xA6690000A66A, + 0xA66B0000A66C, + 0xA66D0000A670, + 0xA6740000A67E, + 0xA67F0000A680, + 0xA6810000A682, + 0xA6830000A684, + 0xA6850000A686, + 0xA6870000A688, + 0xA6890000A68A, + 0xA68B0000A68C, + 0xA68D0000A68E, + 0xA68F0000A690, + 0xA6910000A692, + 0xA6930000A694, + 0xA6950000A696, + 0xA6970000A698, + 0xA6990000A69A, + 0xA69B0000A69C, + 0xA69E0000A6E6, + 0xA6F00000A6F2, + 0xA7170000A720, + 0xA7230000A724, + 0xA7250000A726, + 0xA7270000A728, + 0xA7290000A72A, + 0xA72B0000A72C, + 0xA72D0000A72E, + 0xA72F0000A732, + 0xA7330000A734, + 0xA7350000A736, + 0xA7370000A738, + 0xA7390000A73A, + 0xA73B0000A73C, + 0xA73D0000A73E, + 0xA73F0000A740, + 0xA7410000A742, + 0xA7430000A744, + 0xA7450000A746, + 0xA7470000A748, + 0xA7490000A74A, + 0xA74B0000A74C, + 0xA74D0000A74E, + 0xA74F0000A750, + 0xA7510000A752, + 0xA7530000A754, + 0xA7550000A756, + 0xA7570000A758, + 0xA7590000A75A, + 0xA75B0000A75C, + 0xA75D0000A75E, + 0xA75F0000A760, + 0xA7610000A762, + 0xA7630000A764, + 0xA7650000A766, + 0xA7670000A768, + 0xA7690000A76A, + 0xA76B0000A76C, + 0xA76D0000A76E, + 0xA76F0000A770, + 0xA7710000A779, + 0xA77A0000A77B, + 0xA77C0000A77D, + 0xA77F0000A780, + 0xA7810000A782, + 0xA7830000A784, + 0xA7850000A786, + 0xA7870000A789, + 0xA78C0000A78D, + 0xA78E0000A790, + 0xA7910000A792, + 0xA7930000A796, + 0xA7970000A798, + 0xA7990000A79A, + 0xA79B0000A79C, + 0xA79D0000A79E, + 0xA79F0000A7A0, + 0xA7A10000A7A2, + 0xA7A30000A7A4, + 0xA7A50000A7A6, + 0xA7A70000A7A8, + 0xA7A90000A7AA, + 0xA7AF0000A7B0, + 0xA7B50000A7B6, + 0xA7B70000A7B8, + 0xA7B90000A7BA, + 0xA7BB0000A7BC, + 0xA7BD0000A7BE, + 0xA7BF0000A7C0, + 0xA7C10000A7C2, + 0xA7C30000A7C4, + 0xA7C80000A7C9, + 0xA7CA0000A7CB, + 0xA7D10000A7D2, + 0xA7D30000A7D4, + 0xA7D50000A7D6, + 0xA7D70000A7D8, + 0xA7D90000A7DA, + 0xA7F60000A7F8, + 0xA7FA0000A828, + 0xA82C0000A82D, + 0xA8400000A874, + 0xA8800000A8C6, + 0xA8D00000A8DA, + 0xA8E00000A8F8, + 0xA8FB0000A8FC, + 0xA8FD0000A92E, + 0xA9300000A954, + 0xA9800000A9C1, + 0xA9CF0000A9DA, + 0xA9E00000A9FF, + 0xAA000000AA37, + 0xAA400000AA4E, + 0xAA500000AA5A, + 0xAA600000AA77, + 0xAA7A0000AAC3, + 0xAADB0000AADE, + 0xAAE00000AAF0, + 0xAAF20000AAF7, + 0xAB010000AB07, + 0xAB090000AB0F, + 0xAB110000AB17, + 0xAB200000AB27, + 0xAB280000AB2F, + 0xAB300000AB5B, + 0xAB600000AB69, + 0xABC00000ABEB, + 0xABEC0000ABEE, + 0xABF00000ABFA, + 0xAC000000D7A4, + 0xFA0E0000FA10, + 0xFA110000FA12, + 0xFA130000FA15, + 0xFA1F0000FA20, + 0xFA210000FA22, + 0xFA230000FA25, + 0xFA270000FA2A, + 0xFB1E0000FB1F, + 0xFE200000FE30, + 0xFE730000FE74, + 0x100000001000C, + 0x1000D00010027, + 0x100280001003B, + 0x1003C0001003E, + 0x1003F0001004E, + 0x100500001005E, + 0x10080000100FB, + 0x101FD000101FE, + 0x102800001029D, + 0x102A0000102D1, + 0x102E0000102E1, + 0x1030000010320, + 0x1032D00010341, + 0x103420001034A, + 0x103500001037B, + 0x103800001039E, + 0x103A0000103C4, + 0x103C8000103D0, + 0x104280001049E, + 0x104A0000104AA, + 0x104D8000104FC, + 0x1050000010528, + 0x1053000010564, + 0x10597000105A2, + 0x105A3000105B2, + 0x105B3000105BA, + 0x105BB000105BD, + 0x1060000010737, + 0x1074000010756, + 0x1076000010768, + 0x1078000010781, + 0x1080000010806, + 0x1080800010809, + 0x1080A00010836, + 0x1083700010839, + 0x1083C0001083D, + 0x1083F00010856, + 0x1086000010877, + 0x108800001089F, + 0x108E0000108F3, + 0x108F4000108F6, + 0x1090000010916, + 0x109200001093A, + 0x10980000109B8, + 0x109BE000109C0, + 0x10A0000010A04, + 0x10A0500010A07, + 0x10A0C00010A14, + 0x10A1500010A18, + 0x10A1900010A36, + 0x10A3800010A3B, + 0x10A3F00010A40, + 0x10A6000010A7D, + 0x10A8000010A9D, + 0x10AC000010AC8, + 0x10AC900010AE7, + 0x10B0000010B36, + 0x10B4000010B56, + 0x10B6000010B73, + 0x10B8000010B92, + 0x10C0000010C49, + 0x10CC000010CF3, + 0x10D0000010D28, + 0x10D3000010D3A, + 0x10E8000010EAA, + 0x10EAB00010EAD, + 0x10EB000010EB2, + 0x10EFD00010F1D, + 0x10F2700010F28, + 0x10F3000010F51, + 0x10F7000010F86, + 0x10FB000010FC5, + 0x10FE000010FF7, + 0x1100000011047, + 0x1106600011076, + 0x1107F000110BB, + 0x110C2000110C3, + 0x110D0000110E9, + 0x110F0000110FA, + 0x1110000011135, + 0x1113600011140, + 0x1114400011148, + 0x1115000011174, + 0x1117600011177, + 0x11180000111C5, + 0x111C9000111CD, + 0x111CE000111DB, + 0x111DC000111DD, + 0x1120000011212, + 0x1121300011238, + 0x1123E00011242, + 0x1128000011287, + 0x1128800011289, + 0x1128A0001128E, + 0x1128F0001129E, + 0x1129F000112A9, + 0x112B0000112EB, + 0x112F0000112FA, + 0x1130000011304, + 0x113050001130D, + 0x1130F00011311, + 0x1131300011329, + 0x1132A00011331, + 0x1133200011334, + 0x113350001133A, + 0x1133B00011345, + 0x1134700011349, + 0x1134B0001134E, + 0x1135000011351, + 0x1135700011358, + 0x1135D00011364, + 0x113660001136D, + 0x1137000011375, + 0x114000001144B, + 0x114500001145A, + 0x1145E00011462, + 0x11480000114C6, + 0x114C7000114C8, + 0x114D0000114DA, + 0x11580000115B6, + 0x115B8000115C1, + 0x115D8000115DE, + 0x1160000011641, + 0x1164400011645, + 0x116500001165A, + 0x11680000116B9, + 0x116C0000116CA, + 0x117000001171B, + 0x1171D0001172C, + 0x117300001173A, + 0x1174000011747, + 0x118000001183B, + 0x118C0000118EA, + 0x118FF00011907, + 0x119090001190A, + 0x1190C00011914, + 0x1191500011917, + 0x1191800011936, + 0x1193700011939, + 0x1193B00011944, + 0x119500001195A, + 0x119A0000119A8, + 0x119AA000119D8, + 0x119DA000119E2, + 0x119E3000119E5, + 0x11A0000011A3F, + 0x11A4700011A48, + 0x11A5000011A9A, + 0x11A9D00011A9E, + 0x11AB000011AF9, + 0x11C0000011C09, + 0x11C0A00011C37, + 0x11C3800011C41, + 0x11C5000011C5A, + 0x11C7200011C90, + 0x11C9200011CA8, + 0x11CA900011CB7, + 0x11D0000011D07, + 0x11D0800011D0A, + 0x11D0B00011D37, + 0x11D3A00011D3B, + 0x11D3C00011D3E, + 0x11D3F00011D48, + 0x11D5000011D5A, + 0x11D6000011D66, + 0x11D6700011D69, + 0x11D6A00011D8F, + 0x11D9000011D92, + 0x11D9300011D99, + 0x11DA000011DAA, + 0x11EE000011EF7, + 0x11F0000011F11, + 0x11F1200011F3B, + 0x11F3E00011F43, + 0x11F5000011F5A, + 0x11FB000011FB1, + 0x120000001239A, + 0x1248000012544, + 0x12F9000012FF1, + 0x1300000013430, + 0x1344000013456, + 0x1440000014647, + 0x1680000016A39, + 0x16A4000016A5F, + 0x16A6000016A6A, + 0x16A7000016ABF, + 0x16AC000016ACA, + 0x16AD000016AEE, + 0x16AF000016AF5, + 0x16B0000016B37, + 0x16B4000016B44, + 0x16B5000016B5A, + 0x16B6300016B78, + 0x16B7D00016B90, + 0x16E6000016E80, + 0x16F0000016F4B, + 0x16F4F00016F88, + 0x16F8F00016FA0, + 0x16FE000016FE2, + 0x16FE300016FE5, + 0x16FF000016FF2, + 0x17000000187F8, + 0x1880000018CD6, + 0x18D0000018D09, + 0x1AFF00001AFF4, + 0x1AFF50001AFFC, + 0x1AFFD0001AFFF, + 0x1B0000001B123, + 0x1B1320001B133, + 0x1B1500001B153, + 0x1B1550001B156, + 0x1B1640001B168, + 0x1B1700001B2FC, + 0x1BC000001BC6B, + 0x1BC700001BC7D, + 0x1BC800001BC89, + 0x1BC900001BC9A, + 0x1BC9D0001BC9F, + 0x1CF000001CF2E, + 0x1CF300001CF47, + 0x1DA000001DA37, + 0x1DA3B0001DA6D, + 0x1DA750001DA76, + 0x1DA840001DA85, + 0x1DA9B0001DAA0, + 0x1DAA10001DAB0, + 0x1DF000001DF1F, + 0x1DF250001DF2B, + 0x1E0000001E007, + 0x1E0080001E019, + 0x1E01B0001E022, + 0x1E0230001E025, + 0x1E0260001E02B, + 0x1E08F0001E090, + 0x1E1000001E12D, + 0x1E1300001E13E, + 0x1E1400001E14A, + 0x1E14E0001E14F, + 0x1E2900001E2AF, + 0x1E2C00001E2FA, + 0x1E4D00001E4FA, + 0x1E7E00001E7E7, + 0x1E7E80001E7EC, + 0x1E7ED0001E7EF, + 0x1E7F00001E7FF, + 0x1E8000001E8C5, + 0x1E8D00001E8D7, + 0x1E9220001E94C, + 0x1E9500001E95A, + 0x200000002A6E0, + 0x2A7000002B73A, + 0x2B7400002B81E, + 0x2B8200002CEA2, + 0x2CEB00002EBE1, + 0x2EBF00002EE5E, + 0x300000003134B, + 0x31350000323B0, + ), + "CONTEXTJ": (0x200C0000200E,), + "CONTEXTO": ( + 0xB7000000B8, + 0x37500000376, + 0x5F3000005F5, + 0x6600000066A, + 0x6F0000006FA, + 0x30FB000030FC, + ), +} diff --git a/venv/lib/python3.12/site-packages/idna/intranges.py b/venv/lib/python3.12/site-packages/idna/intranges.py new file mode 100644 index 00000000..7bfaa8d8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/idna/intranges.py @@ -0,0 +1,57 @@ +""" +Given a list of integers, made up of (hopefully) a small number of long runs +of consecutive integers, compute a representation of the form +((start1, end1), (start2, end2) ...). Then answer the question "was x present +in the original list?" in time O(log(# runs)). +""" + +import bisect +from typing import List, Tuple + + +def intranges_from_list(list_: List[int]) -> Tuple[int, ...]: + """Represent a list of integers as a sequence of ranges: + ((start_0, end_0), (start_1, end_1), ...), such that the original + integers are exactly those x such that start_i <= x < end_i for some i. + + Ranges are encoded as single integers (start << 32 | end), not as tuples. + """ + + sorted_list = sorted(list_) + ranges = [] + last_write = -1 + for i in range(len(sorted_list)): + if i + 1 < len(sorted_list): + if sorted_list[i] == sorted_list[i + 1] - 1: + continue + current_range = sorted_list[last_write + 1 : i + 1] + ranges.append(_encode_range(current_range[0], current_range[-1] + 1)) + last_write = i + + return tuple(ranges) + + +def _encode_range(start: int, end: int) -> int: + return (start << 32) | end + + +def _decode_range(r: int) -> Tuple[int, int]: + return (r >> 32), (r & ((1 << 32) - 1)) + + +def intranges_contain(int_: int, ranges: Tuple[int, ...]) -> bool: + """Determine if `int_` falls into one of the ranges in `ranges`.""" + tuple_ = _encode_range(int_, 0) + pos = bisect.bisect_left(ranges, tuple_) + # we could be immediately ahead of a tuple (start, end) + # with start < int_ <= end + if pos > 0: + left, right = _decode_range(ranges[pos - 1]) + if left <= int_ < right: + return True + # or we could be immediately behind a tuple (int_, end) + if pos < len(ranges): + left, _ = _decode_range(ranges[pos]) + if left == int_: + return True + return False diff --git a/venv/lib/python3.12/site-packages/idna/package_data.py b/venv/lib/python3.12/site-packages/idna/package_data.py new file mode 100644 index 00000000..514ff7e2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/idna/package_data.py @@ -0,0 +1 @@ +__version__ = "3.10" diff --git a/venv/lib/python3.12/site-packages/idna/py.typed b/venv/lib/python3.12/site-packages/idna/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/venv/lib/python3.12/site-packages/idna/uts46data.py b/venv/lib/python3.12/site-packages/idna/uts46data.py new file mode 100644 index 00000000..eb894327 --- /dev/null +++ b/venv/lib/python3.12/site-packages/idna/uts46data.py @@ -0,0 +1,8681 @@ +# This file is automatically generated by tools/idna-data +# vim: set fileencoding=utf-8 : + +from typing import List, Tuple, Union + +"""IDNA Mapping Table from UTS46.""" + + +__version__ = "15.1.0" + + +def _seg_0() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x0, "3"), + (0x1, "3"), + (0x2, "3"), + (0x3, "3"), + (0x4, "3"), + (0x5, "3"), + (0x6, "3"), + (0x7, "3"), + (0x8, "3"), + (0x9, "3"), + (0xA, "3"), + (0xB, "3"), + (0xC, "3"), + (0xD, "3"), + (0xE, "3"), + (0xF, "3"), + (0x10, "3"), + (0x11, "3"), + (0x12, "3"), + (0x13, "3"), + (0x14, "3"), + (0x15, "3"), + (0x16, "3"), + (0x17, "3"), + (0x18, "3"), + (0x19, "3"), + (0x1A, "3"), + (0x1B, "3"), + (0x1C, "3"), + (0x1D, "3"), + (0x1E, "3"), + (0x1F, "3"), + (0x20, "3"), + (0x21, "3"), + (0x22, "3"), + (0x23, "3"), + (0x24, "3"), + (0x25, "3"), + (0x26, "3"), + (0x27, "3"), + (0x28, "3"), + (0x29, "3"), + (0x2A, "3"), + (0x2B, "3"), + (0x2C, "3"), + (0x2D, "V"), + (0x2E, "V"), + (0x2F, "3"), + (0x30, "V"), + (0x31, "V"), + (0x32, "V"), + (0x33, "V"), + (0x34, "V"), + (0x35, "V"), + (0x36, "V"), + (0x37, "V"), + (0x38, "V"), + (0x39, "V"), + (0x3A, "3"), + (0x3B, "3"), + (0x3C, "3"), + (0x3D, "3"), + (0x3E, "3"), + (0x3F, "3"), + (0x40, "3"), + (0x41, "M", "a"), + (0x42, "M", "b"), + (0x43, "M", "c"), + (0x44, "M", "d"), + (0x45, "M", "e"), + (0x46, "M", "f"), + (0x47, "M", "g"), + (0x48, "M", "h"), + (0x49, "M", "i"), + (0x4A, "M", "j"), + (0x4B, "M", "k"), + (0x4C, "M", "l"), + (0x4D, "M", "m"), + (0x4E, "M", "n"), + (0x4F, "M", "o"), + (0x50, "M", "p"), + (0x51, "M", "q"), + (0x52, "M", "r"), + (0x53, "M", "s"), + (0x54, "M", "t"), + (0x55, "M", "u"), + (0x56, "M", "v"), + (0x57, "M", "w"), + (0x58, "M", "x"), + (0x59, "M", "y"), + (0x5A, "M", "z"), + (0x5B, "3"), + (0x5C, "3"), + (0x5D, "3"), + (0x5E, "3"), + (0x5F, "3"), + (0x60, "3"), + (0x61, "V"), + (0x62, "V"), + (0x63, "V"), + ] + + +def _seg_1() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x64, "V"), + (0x65, "V"), + (0x66, "V"), + (0x67, "V"), + (0x68, "V"), + (0x69, "V"), + (0x6A, "V"), + (0x6B, "V"), + (0x6C, "V"), + (0x6D, "V"), + (0x6E, "V"), + (0x6F, "V"), + (0x70, "V"), + (0x71, "V"), + (0x72, "V"), + (0x73, "V"), + (0x74, "V"), + (0x75, "V"), + (0x76, "V"), + (0x77, "V"), + (0x78, "V"), + (0x79, "V"), + (0x7A, "V"), + (0x7B, "3"), + (0x7C, "3"), + (0x7D, "3"), + (0x7E, "3"), + (0x7F, "3"), + (0x80, "X"), + (0x81, "X"), + (0x82, "X"), + (0x83, "X"), + (0x84, "X"), + (0x85, "X"), + (0x86, "X"), + (0x87, "X"), + (0x88, "X"), + (0x89, "X"), + (0x8A, "X"), + (0x8B, "X"), + (0x8C, "X"), + (0x8D, "X"), + (0x8E, "X"), + (0x8F, "X"), + (0x90, "X"), + (0x91, "X"), + (0x92, "X"), + (0x93, "X"), + (0x94, "X"), + (0x95, "X"), + (0x96, "X"), + (0x97, "X"), + (0x98, "X"), + (0x99, "X"), + (0x9A, "X"), + (0x9B, "X"), + (0x9C, "X"), + (0x9D, "X"), + (0x9E, "X"), + (0x9F, "X"), + (0xA0, "3", " "), + (0xA1, "V"), + (0xA2, "V"), + (0xA3, "V"), + (0xA4, "V"), + (0xA5, "V"), + (0xA6, "V"), + (0xA7, "V"), + (0xA8, "3", " ̈"), + (0xA9, "V"), + (0xAA, "M", "a"), + (0xAB, "V"), + (0xAC, "V"), + (0xAD, "I"), + (0xAE, "V"), + (0xAF, "3", " ̄"), + (0xB0, "V"), + (0xB1, "V"), + (0xB2, "M", "2"), + (0xB3, "M", "3"), + (0xB4, "3", " ́"), + (0xB5, "M", "μ"), + (0xB6, "V"), + (0xB7, "V"), + (0xB8, "3", " ̧"), + (0xB9, "M", "1"), + (0xBA, "M", "o"), + (0xBB, "V"), + (0xBC, "M", "1⁄4"), + (0xBD, "M", "1⁄2"), + (0xBE, "M", "3⁄4"), + (0xBF, "V"), + (0xC0, "M", "à"), + (0xC1, "M", "á"), + (0xC2, "M", "â"), + (0xC3, "M", "ã"), + (0xC4, "M", "ä"), + (0xC5, "M", "å"), + (0xC6, "M", "æ"), + (0xC7, "M", "ç"), + ] + + +def _seg_2() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xC8, "M", "è"), + (0xC9, "M", "é"), + (0xCA, "M", "ê"), + (0xCB, "M", "ë"), + (0xCC, "M", "ì"), + (0xCD, "M", "í"), + (0xCE, "M", "î"), + (0xCF, "M", "ï"), + (0xD0, "M", "ð"), + (0xD1, "M", "ñ"), + (0xD2, "M", "ò"), + (0xD3, "M", "ó"), + (0xD4, "M", "ô"), + (0xD5, "M", "õ"), + (0xD6, "M", "ö"), + (0xD7, "V"), + (0xD8, "M", "ø"), + (0xD9, "M", "ù"), + (0xDA, "M", "ú"), + (0xDB, "M", "û"), + (0xDC, "M", "ü"), + (0xDD, "M", "ý"), + (0xDE, "M", "þ"), + (0xDF, "D", "ss"), + (0xE0, "V"), + (0xE1, "V"), + (0xE2, "V"), + (0xE3, "V"), + (0xE4, "V"), + (0xE5, "V"), + (0xE6, "V"), + (0xE7, "V"), + (0xE8, "V"), + (0xE9, "V"), + (0xEA, "V"), + (0xEB, "V"), + (0xEC, "V"), + (0xED, "V"), + (0xEE, "V"), + (0xEF, "V"), + (0xF0, "V"), + (0xF1, "V"), + (0xF2, "V"), + (0xF3, "V"), + (0xF4, "V"), + (0xF5, "V"), + (0xF6, "V"), + (0xF7, "V"), + (0xF8, "V"), + (0xF9, "V"), + (0xFA, "V"), + (0xFB, "V"), + (0xFC, "V"), + (0xFD, "V"), + (0xFE, "V"), + (0xFF, "V"), + (0x100, "M", "ā"), + (0x101, "V"), + (0x102, "M", "ă"), + (0x103, "V"), + (0x104, "M", "ą"), + (0x105, "V"), + (0x106, "M", "ć"), + (0x107, "V"), + (0x108, "M", "ĉ"), + (0x109, "V"), + (0x10A, "M", "ċ"), + (0x10B, "V"), + (0x10C, "M", "č"), + (0x10D, "V"), + (0x10E, "M", "ď"), + (0x10F, "V"), + (0x110, "M", "đ"), + (0x111, "V"), + (0x112, "M", "ē"), + (0x113, "V"), + (0x114, "M", "ĕ"), + (0x115, "V"), + (0x116, "M", "ė"), + (0x117, "V"), + (0x118, "M", "ę"), + (0x119, "V"), + (0x11A, "M", "ě"), + (0x11B, "V"), + (0x11C, "M", "ĝ"), + (0x11D, "V"), + (0x11E, "M", "ğ"), + (0x11F, "V"), + (0x120, "M", "ġ"), + (0x121, "V"), + (0x122, "M", "ģ"), + (0x123, "V"), + (0x124, "M", "ĥ"), + (0x125, "V"), + (0x126, "M", "ħ"), + (0x127, "V"), + (0x128, "M", "ĩ"), + (0x129, "V"), + (0x12A, "M", "ī"), + (0x12B, "V"), + ] + + +def _seg_3() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x12C, "M", "ĭ"), + (0x12D, "V"), + (0x12E, "M", "į"), + (0x12F, "V"), + (0x130, "M", "i̇"), + (0x131, "V"), + (0x132, "M", "ij"), + (0x134, "M", "ĵ"), + (0x135, "V"), + (0x136, "M", "ķ"), + (0x137, "V"), + (0x139, "M", "ĺ"), + (0x13A, "V"), + (0x13B, "M", "ļ"), + (0x13C, "V"), + (0x13D, "M", "ľ"), + (0x13E, "V"), + (0x13F, "M", "l·"), + (0x141, "M", "ł"), + (0x142, "V"), + (0x143, "M", "ń"), + (0x144, "V"), + (0x145, "M", "ņ"), + (0x146, "V"), + (0x147, "M", "ň"), + (0x148, "V"), + (0x149, "M", "ʼn"), + (0x14A, "M", "ŋ"), + (0x14B, "V"), + (0x14C, "M", "ō"), + (0x14D, "V"), + (0x14E, "M", "ŏ"), + (0x14F, "V"), + (0x150, "M", "ő"), + (0x151, "V"), + (0x152, "M", "œ"), + (0x153, "V"), + (0x154, "M", "ŕ"), + (0x155, "V"), + (0x156, "M", "ŗ"), + (0x157, "V"), + (0x158, "M", "ř"), + (0x159, "V"), + (0x15A, "M", "ś"), + (0x15B, "V"), + (0x15C, "M", "ŝ"), + (0x15D, "V"), + (0x15E, "M", "ş"), + (0x15F, "V"), + (0x160, "M", "š"), + (0x161, "V"), + (0x162, "M", "ţ"), + (0x163, "V"), + (0x164, "M", "ť"), + (0x165, "V"), + (0x166, "M", "ŧ"), + (0x167, "V"), + (0x168, "M", "ũ"), + (0x169, "V"), + (0x16A, "M", "ū"), + (0x16B, "V"), + (0x16C, "M", "ŭ"), + (0x16D, "V"), + (0x16E, "M", "ů"), + (0x16F, "V"), + (0x170, "M", "ű"), + (0x171, "V"), + (0x172, "M", "ų"), + (0x173, "V"), + (0x174, "M", "ŵ"), + (0x175, "V"), + (0x176, "M", "ŷ"), + (0x177, "V"), + (0x178, "M", "ÿ"), + (0x179, "M", "ź"), + (0x17A, "V"), + (0x17B, "M", "ż"), + (0x17C, "V"), + (0x17D, "M", "ž"), + (0x17E, "V"), + (0x17F, "M", "s"), + (0x180, "V"), + (0x181, "M", "ɓ"), + (0x182, "M", "ƃ"), + (0x183, "V"), + (0x184, "M", "ƅ"), + (0x185, "V"), + (0x186, "M", "ɔ"), + (0x187, "M", "ƈ"), + (0x188, "V"), + (0x189, "M", "ɖ"), + (0x18A, "M", "ɗ"), + (0x18B, "M", "ƌ"), + (0x18C, "V"), + (0x18E, "M", "ǝ"), + (0x18F, "M", "ə"), + (0x190, "M", "ɛ"), + (0x191, "M", "ƒ"), + (0x192, "V"), + (0x193, "M", "ɠ"), + ] + + +def _seg_4() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x194, "M", "ɣ"), + (0x195, "V"), + (0x196, "M", "ɩ"), + (0x197, "M", "ɨ"), + (0x198, "M", "ƙ"), + (0x199, "V"), + (0x19C, "M", "ɯ"), + (0x19D, "M", "ɲ"), + (0x19E, "V"), + (0x19F, "M", "ɵ"), + (0x1A0, "M", "ơ"), + (0x1A1, "V"), + (0x1A2, "M", "ƣ"), + (0x1A3, "V"), + (0x1A4, "M", "ƥ"), + (0x1A5, "V"), + (0x1A6, "M", "ʀ"), + (0x1A7, "M", "ƨ"), + (0x1A8, "V"), + (0x1A9, "M", "ʃ"), + (0x1AA, "V"), + (0x1AC, "M", "ƭ"), + (0x1AD, "V"), + (0x1AE, "M", "ʈ"), + (0x1AF, "M", "ư"), + (0x1B0, "V"), + (0x1B1, "M", "ʊ"), + (0x1B2, "M", "ʋ"), + (0x1B3, "M", "ƴ"), + (0x1B4, "V"), + (0x1B5, "M", "ƶ"), + (0x1B6, "V"), + (0x1B7, "M", "ʒ"), + (0x1B8, "M", "ƹ"), + (0x1B9, "V"), + (0x1BC, "M", "ƽ"), + (0x1BD, "V"), + (0x1C4, "M", "dž"), + (0x1C7, "M", "lj"), + (0x1CA, "M", "nj"), + (0x1CD, "M", "ǎ"), + (0x1CE, "V"), + (0x1CF, "M", "ǐ"), + (0x1D0, "V"), + (0x1D1, "M", "ǒ"), + (0x1D2, "V"), + (0x1D3, "M", "ǔ"), + (0x1D4, "V"), + (0x1D5, "M", "ǖ"), + (0x1D6, "V"), + (0x1D7, "M", "ǘ"), + (0x1D8, "V"), + (0x1D9, "M", "ǚ"), + (0x1DA, "V"), + (0x1DB, "M", "ǜ"), + (0x1DC, "V"), + (0x1DE, "M", "ǟ"), + (0x1DF, "V"), + (0x1E0, "M", "ǡ"), + (0x1E1, "V"), + (0x1E2, "M", "ǣ"), + (0x1E3, "V"), + (0x1E4, "M", "ǥ"), + (0x1E5, "V"), + (0x1E6, "M", "ǧ"), + (0x1E7, "V"), + (0x1E8, "M", "ǩ"), + (0x1E9, "V"), + (0x1EA, "M", "ǫ"), + (0x1EB, "V"), + (0x1EC, "M", "ǭ"), + (0x1ED, "V"), + (0x1EE, "M", "ǯ"), + (0x1EF, "V"), + (0x1F1, "M", "dz"), + (0x1F4, "M", "ǵ"), + (0x1F5, "V"), + (0x1F6, "M", "ƕ"), + (0x1F7, "M", "ƿ"), + (0x1F8, "M", "ǹ"), + (0x1F9, "V"), + (0x1FA, "M", "ǻ"), + (0x1FB, "V"), + (0x1FC, "M", "ǽ"), + (0x1FD, "V"), + (0x1FE, "M", "ǿ"), + (0x1FF, "V"), + (0x200, "M", "ȁ"), + (0x201, "V"), + (0x202, "M", "ȃ"), + (0x203, "V"), + (0x204, "M", "ȅ"), + (0x205, "V"), + (0x206, "M", "ȇ"), + (0x207, "V"), + (0x208, "M", "ȉ"), + (0x209, "V"), + (0x20A, "M", "ȋ"), + (0x20B, "V"), + (0x20C, "M", "ȍ"), + ] + + +def _seg_5() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x20D, "V"), + (0x20E, "M", "ȏ"), + (0x20F, "V"), + (0x210, "M", "ȑ"), + (0x211, "V"), + (0x212, "M", "ȓ"), + (0x213, "V"), + (0x214, "M", "ȕ"), + (0x215, "V"), + (0x216, "M", "ȗ"), + (0x217, "V"), + (0x218, "M", "ș"), + (0x219, "V"), + (0x21A, "M", "ț"), + (0x21B, "V"), + (0x21C, "M", "ȝ"), + (0x21D, "V"), + (0x21E, "M", "ȟ"), + (0x21F, "V"), + (0x220, "M", "ƞ"), + (0x221, "V"), + (0x222, "M", "ȣ"), + (0x223, "V"), + (0x224, "M", "ȥ"), + (0x225, "V"), + (0x226, "M", "ȧ"), + (0x227, "V"), + (0x228, "M", "ȩ"), + (0x229, "V"), + (0x22A, "M", "ȫ"), + (0x22B, "V"), + (0x22C, "M", "ȭ"), + (0x22D, "V"), + (0x22E, "M", "ȯ"), + (0x22F, "V"), + (0x230, "M", "ȱ"), + (0x231, "V"), + (0x232, "M", "ȳ"), + (0x233, "V"), + (0x23A, "M", "ⱥ"), + (0x23B, "M", "ȼ"), + (0x23C, "V"), + (0x23D, "M", "ƚ"), + (0x23E, "M", "ⱦ"), + (0x23F, "V"), + (0x241, "M", "ɂ"), + (0x242, "V"), + (0x243, "M", "ƀ"), + (0x244, "M", "ʉ"), + (0x245, "M", "ʌ"), + (0x246, "M", "ɇ"), + (0x247, "V"), + (0x248, "M", "ɉ"), + (0x249, "V"), + (0x24A, "M", "ɋ"), + (0x24B, "V"), + (0x24C, "M", "ɍ"), + (0x24D, "V"), + (0x24E, "M", "ɏ"), + (0x24F, "V"), + (0x2B0, "M", "h"), + (0x2B1, "M", "ɦ"), + (0x2B2, "M", "j"), + (0x2B3, "M", "r"), + (0x2B4, "M", "ɹ"), + (0x2B5, "M", "ɻ"), + (0x2B6, "M", "ʁ"), + (0x2B7, "M", "w"), + (0x2B8, "M", "y"), + (0x2B9, "V"), + (0x2D8, "3", " ̆"), + (0x2D9, "3", " ̇"), + (0x2DA, "3", " ̊"), + (0x2DB, "3", " ̨"), + (0x2DC, "3", " ̃"), + (0x2DD, "3", " ̋"), + (0x2DE, "V"), + (0x2E0, "M", "ɣ"), + (0x2E1, "M", "l"), + (0x2E2, "M", "s"), + (0x2E3, "M", "x"), + (0x2E4, "M", "ʕ"), + (0x2E5, "V"), + (0x340, "M", "̀"), + (0x341, "M", "́"), + (0x342, "V"), + (0x343, "M", "̓"), + (0x344, "M", "̈́"), + (0x345, "M", "ι"), + (0x346, "V"), + (0x34F, "I"), + (0x350, "V"), + (0x370, "M", "ͱ"), + (0x371, "V"), + (0x372, "M", "ͳ"), + (0x373, "V"), + (0x374, "M", "ʹ"), + (0x375, "V"), + (0x376, "M", "ͷ"), + (0x377, "V"), + ] + + +def _seg_6() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x378, "X"), + (0x37A, "3", " ι"), + (0x37B, "V"), + (0x37E, "3", ";"), + (0x37F, "M", "ϳ"), + (0x380, "X"), + (0x384, "3", " ́"), + (0x385, "3", " ̈́"), + (0x386, "M", "ά"), + (0x387, "M", "·"), + (0x388, "M", "έ"), + (0x389, "M", "ή"), + (0x38A, "M", "ί"), + (0x38B, "X"), + (0x38C, "M", "ό"), + (0x38D, "X"), + (0x38E, "M", "ύ"), + (0x38F, "M", "ώ"), + (0x390, "V"), + (0x391, "M", "α"), + (0x392, "M", "β"), + (0x393, "M", "γ"), + (0x394, "M", "δ"), + (0x395, "M", "ε"), + (0x396, "M", "ζ"), + (0x397, "M", "η"), + (0x398, "M", "θ"), + (0x399, "M", "ι"), + (0x39A, "M", "κ"), + (0x39B, "M", "λ"), + (0x39C, "M", "μ"), + (0x39D, "M", "ν"), + (0x39E, "M", "ξ"), + (0x39F, "M", "ο"), + (0x3A0, "M", "π"), + (0x3A1, "M", "ρ"), + (0x3A2, "X"), + (0x3A3, "M", "σ"), + (0x3A4, "M", "τ"), + (0x3A5, "M", "υ"), + (0x3A6, "M", "φ"), + (0x3A7, "M", "χ"), + (0x3A8, "M", "ψ"), + (0x3A9, "M", "ω"), + (0x3AA, "M", "ϊ"), + (0x3AB, "M", "ϋ"), + (0x3AC, "V"), + (0x3C2, "D", "σ"), + (0x3C3, "V"), + (0x3CF, "M", "ϗ"), + (0x3D0, "M", "β"), + (0x3D1, "M", "θ"), + (0x3D2, "M", "υ"), + (0x3D3, "M", "ύ"), + (0x3D4, "M", "ϋ"), + (0x3D5, "M", "φ"), + (0x3D6, "M", "π"), + (0x3D7, "V"), + (0x3D8, "M", "ϙ"), + (0x3D9, "V"), + (0x3DA, "M", "ϛ"), + (0x3DB, "V"), + (0x3DC, "M", "ϝ"), + (0x3DD, "V"), + (0x3DE, "M", "ϟ"), + (0x3DF, "V"), + (0x3E0, "M", "ϡ"), + (0x3E1, "V"), + (0x3E2, "M", "ϣ"), + (0x3E3, "V"), + (0x3E4, "M", "ϥ"), + (0x3E5, "V"), + (0x3E6, "M", "ϧ"), + (0x3E7, "V"), + (0x3E8, "M", "ϩ"), + (0x3E9, "V"), + (0x3EA, "M", "ϫ"), + (0x3EB, "V"), + (0x3EC, "M", "ϭ"), + (0x3ED, "V"), + (0x3EE, "M", "ϯ"), + (0x3EF, "V"), + (0x3F0, "M", "κ"), + (0x3F1, "M", "ρ"), + (0x3F2, "M", "σ"), + (0x3F3, "V"), + (0x3F4, "M", "θ"), + (0x3F5, "M", "ε"), + (0x3F6, "V"), + (0x3F7, "M", "ϸ"), + (0x3F8, "V"), + (0x3F9, "M", "σ"), + (0x3FA, "M", "ϻ"), + (0x3FB, "V"), + (0x3FD, "M", "ͻ"), + (0x3FE, "M", "ͼ"), + (0x3FF, "M", "ͽ"), + (0x400, "M", "ѐ"), + (0x401, "M", "ё"), + (0x402, "M", "ђ"), + ] + + +def _seg_7() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x403, "M", "ѓ"), + (0x404, "M", "є"), + (0x405, "M", "ѕ"), + (0x406, "M", "і"), + (0x407, "M", "ї"), + (0x408, "M", "ј"), + (0x409, "M", "љ"), + (0x40A, "M", "њ"), + (0x40B, "M", "ћ"), + (0x40C, "M", "ќ"), + (0x40D, "M", "ѝ"), + (0x40E, "M", "ў"), + (0x40F, "M", "џ"), + (0x410, "M", "а"), + (0x411, "M", "б"), + (0x412, "M", "в"), + (0x413, "M", "г"), + (0x414, "M", "д"), + (0x415, "M", "е"), + (0x416, "M", "ж"), + (0x417, "M", "з"), + (0x418, "M", "и"), + (0x419, "M", "й"), + (0x41A, "M", "к"), + (0x41B, "M", "л"), + (0x41C, "M", "м"), + (0x41D, "M", "н"), + (0x41E, "M", "о"), + (0x41F, "M", "п"), + (0x420, "M", "р"), + (0x421, "M", "с"), + (0x422, "M", "т"), + (0x423, "M", "у"), + (0x424, "M", "ф"), + (0x425, "M", "х"), + (0x426, "M", "ц"), + (0x427, "M", "ч"), + (0x428, "M", "ш"), + (0x429, "M", "щ"), + (0x42A, "M", "ъ"), + (0x42B, "M", "ы"), + (0x42C, "M", "ь"), + (0x42D, "M", "э"), + (0x42E, "M", "ю"), + (0x42F, "M", "я"), + (0x430, "V"), + (0x460, "M", "ѡ"), + (0x461, "V"), + (0x462, "M", "ѣ"), + (0x463, "V"), + (0x464, "M", "ѥ"), + (0x465, "V"), + (0x466, "M", "ѧ"), + (0x467, "V"), + (0x468, "M", "ѩ"), + (0x469, "V"), + (0x46A, "M", "ѫ"), + (0x46B, "V"), + (0x46C, "M", "ѭ"), + (0x46D, "V"), + (0x46E, "M", "ѯ"), + (0x46F, "V"), + (0x470, "M", "ѱ"), + (0x471, "V"), + (0x472, "M", "ѳ"), + (0x473, "V"), + (0x474, "M", "ѵ"), + (0x475, "V"), + (0x476, "M", "ѷ"), + (0x477, "V"), + (0x478, "M", "ѹ"), + (0x479, "V"), + (0x47A, "M", "ѻ"), + (0x47B, "V"), + (0x47C, "M", "ѽ"), + (0x47D, "V"), + (0x47E, "M", "ѿ"), + (0x47F, "V"), + (0x480, "M", "ҁ"), + (0x481, "V"), + (0x48A, "M", "ҋ"), + (0x48B, "V"), + (0x48C, "M", "ҍ"), + (0x48D, "V"), + (0x48E, "M", "ҏ"), + (0x48F, "V"), + (0x490, "M", "ґ"), + (0x491, "V"), + (0x492, "M", "ғ"), + (0x493, "V"), + (0x494, "M", "ҕ"), + (0x495, "V"), + (0x496, "M", "җ"), + (0x497, "V"), + (0x498, "M", "ҙ"), + (0x499, "V"), + (0x49A, "M", "қ"), + (0x49B, "V"), + (0x49C, "M", "ҝ"), + (0x49D, "V"), + ] + + +def _seg_8() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x49E, "M", "ҟ"), + (0x49F, "V"), + (0x4A0, "M", "ҡ"), + (0x4A1, "V"), + (0x4A2, "M", "ң"), + (0x4A3, "V"), + (0x4A4, "M", "ҥ"), + (0x4A5, "V"), + (0x4A6, "M", "ҧ"), + (0x4A7, "V"), + (0x4A8, "M", "ҩ"), + (0x4A9, "V"), + (0x4AA, "M", "ҫ"), + (0x4AB, "V"), + (0x4AC, "M", "ҭ"), + (0x4AD, "V"), + (0x4AE, "M", "ү"), + (0x4AF, "V"), + (0x4B0, "M", "ұ"), + (0x4B1, "V"), + (0x4B2, "M", "ҳ"), + (0x4B3, "V"), + (0x4B4, "M", "ҵ"), + (0x4B5, "V"), + (0x4B6, "M", "ҷ"), + (0x4B7, "V"), + (0x4B8, "M", "ҹ"), + (0x4B9, "V"), + (0x4BA, "M", "һ"), + (0x4BB, "V"), + (0x4BC, "M", "ҽ"), + (0x4BD, "V"), + (0x4BE, "M", "ҿ"), + (0x4BF, "V"), + (0x4C0, "X"), + (0x4C1, "M", "ӂ"), + (0x4C2, "V"), + (0x4C3, "M", "ӄ"), + (0x4C4, "V"), + (0x4C5, "M", "ӆ"), + (0x4C6, "V"), + (0x4C7, "M", "ӈ"), + (0x4C8, "V"), + (0x4C9, "M", "ӊ"), + (0x4CA, "V"), + (0x4CB, "M", "ӌ"), + (0x4CC, "V"), + (0x4CD, "M", "ӎ"), + (0x4CE, "V"), + (0x4D0, "M", "ӑ"), + (0x4D1, "V"), + (0x4D2, "M", "ӓ"), + (0x4D3, "V"), + (0x4D4, "M", "ӕ"), + (0x4D5, "V"), + (0x4D6, "M", "ӗ"), + (0x4D7, "V"), + (0x4D8, "M", "ә"), + (0x4D9, "V"), + (0x4DA, "M", "ӛ"), + (0x4DB, "V"), + (0x4DC, "M", "ӝ"), + (0x4DD, "V"), + (0x4DE, "M", "ӟ"), + (0x4DF, "V"), + (0x4E0, "M", "ӡ"), + (0x4E1, "V"), + (0x4E2, "M", "ӣ"), + (0x4E3, "V"), + (0x4E4, "M", "ӥ"), + (0x4E5, "V"), + (0x4E6, "M", "ӧ"), + (0x4E7, "V"), + (0x4E8, "M", "ө"), + (0x4E9, "V"), + (0x4EA, "M", "ӫ"), + (0x4EB, "V"), + (0x4EC, "M", "ӭ"), + (0x4ED, "V"), + (0x4EE, "M", "ӯ"), + (0x4EF, "V"), + (0x4F0, "M", "ӱ"), + (0x4F1, "V"), + (0x4F2, "M", "ӳ"), + (0x4F3, "V"), + (0x4F4, "M", "ӵ"), + (0x4F5, "V"), + (0x4F6, "M", "ӷ"), + (0x4F7, "V"), + (0x4F8, "M", "ӹ"), + (0x4F9, "V"), + (0x4FA, "M", "ӻ"), + (0x4FB, "V"), + (0x4FC, "M", "ӽ"), + (0x4FD, "V"), + (0x4FE, "M", "ӿ"), + (0x4FF, "V"), + (0x500, "M", "ԁ"), + (0x501, "V"), + (0x502, "M", "ԃ"), + ] + + +def _seg_9() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x503, "V"), + (0x504, "M", "ԅ"), + (0x505, "V"), + (0x506, "M", "ԇ"), + (0x507, "V"), + (0x508, "M", "ԉ"), + (0x509, "V"), + (0x50A, "M", "ԋ"), + (0x50B, "V"), + (0x50C, "M", "ԍ"), + (0x50D, "V"), + (0x50E, "M", "ԏ"), + (0x50F, "V"), + (0x510, "M", "ԑ"), + (0x511, "V"), + (0x512, "M", "ԓ"), + (0x513, "V"), + (0x514, "M", "ԕ"), + (0x515, "V"), + (0x516, "M", "ԗ"), + (0x517, "V"), + (0x518, "M", "ԙ"), + (0x519, "V"), + (0x51A, "M", "ԛ"), + (0x51B, "V"), + (0x51C, "M", "ԝ"), + (0x51D, "V"), + (0x51E, "M", "ԟ"), + (0x51F, "V"), + (0x520, "M", "ԡ"), + (0x521, "V"), + (0x522, "M", "ԣ"), + (0x523, "V"), + (0x524, "M", "ԥ"), + (0x525, "V"), + (0x526, "M", "ԧ"), + (0x527, "V"), + (0x528, "M", "ԩ"), + (0x529, "V"), + (0x52A, "M", "ԫ"), + (0x52B, "V"), + (0x52C, "M", "ԭ"), + (0x52D, "V"), + (0x52E, "M", "ԯ"), + (0x52F, "V"), + (0x530, "X"), + (0x531, "M", "ա"), + (0x532, "M", "բ"), + (0x533, "M", "գ"), + (0x534, "M", "դ"), + (0x535, "M", "ե"), + (0x536, "M", "զ"), + (0x537, "M", "է"), + (0x538, "M", "ը"), + (0x539, "M", "թ"), + (0x53A, "M", "ժ"), + (0x53B, "M", "ի"), + (0x53C, "M", "լ"), + (0x53D, "M", "խ"), + (0x53E, "M", "ծ"), + (0x53F, "M", "կ"), + (0x540, "M", "հ"), + (0x541, "M", "ձ"), + (0x542, "M", "ղ"), + (0x543, "M", "ճ"), + (0x544, "M", "մ"), + (0x545, "M", "յ"), + (0x546, "M", "ն"), + (0x547, "M", "շ"), + (0x548, "M", "ո"), + (0x549, "M", "չ"), + (0x54A, "M", "պ"), + (0x54B, "M", "ջ"), + (0x54C, "M", "ռ"), + (0x54D, "M", "ս"), + (0x54E, "M", "վ"), + (0x54F, "M", "տ"), + (0x550, "M", "ր"), + (0x551, "M", "ց"), + (0x552, "M", "ւ"), + (0x553, "M", "փ"), + (0x554, "M", "ք"), + (0x555, "M", "օ"), + (0x556, "M", "ֆ"), + (0x557, "X"), + (0x559, "V"), + (0x587, "M", "եւ"), + (0x588, "V"), + (0x58B, "X"), + (0x58D, "V"), + (0x590, "X"), + (0x591, "V"), + (0x5C8, "X"), + (0x5D0, "V"), + (0x5EB, "X"), + (0x5EF, "V"), + (0x5F5, "X"), + (0x606, "V"), + (0x61C, "X"), + (0x61D, "V"), + ] + + +def _seg_10() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x675, "M", "اٴ"), + (0x676, "M", "وٴ"), + (0x677, "M", "ۇٴ"), + (0x678, "M", "يٴ"), + (0x679, "V"), + (0x6DD, "X"), + (0x6DE, "V"), + (0x70E, "X"), + (0x710, "V"), + (0x74B, "X"), + (0x74D, "V"), + (0x7B2, "X"), + (0x7C0, "V"), + (0x7FB, "X"), + (0x7FD, "V"), + (0x82E, "X"), + (0x830, "V"), + (0x83F, "X"), + (0x840, "V"), + (0x85C, "X"), + (0x85E, "V"), + (0x85F, "X"), + (0x860, "V"), + (0x86B, "X"), + (0x870, "V"), + (0x88F, "X"), + (0x898, "V"), + (0x8E2, "X"), + (0x8E3, "V"), + (0x958, "M", "क़"), + (0x959, "M", "ख़"), + (0x95A, "M", "ग़"), + (0x95B, "M", "ज़"), + (0x95C, "M", "ड़"), + (0x95D, "M", "ढ़"), + (0x95E, "M", "फ़"), + (0x95F, "M", "य़"), + (0x960, "V"), + (0x984, "X"), + (0x985, "V"), + (0x98D, "X"), + (0x98F, "V"), + (0x991, "X"), + (0x993, "V"), + (0x9A9, "X"), + (0x9AA, "V"), + (0x9B1, "X"), + (0x9B2, "V"), + (0x9B3, "X"), + (0x9B6, "V"), + (0x9BA, "X"), + (0x9BC, "V"), + (0x9C5, "X"), + (0x9C7, "V"), + (0x9C9, "X"), + (0x9CB, "V"), + (0x9CF, "X"), + (0x9D7, "V"), + (0x9D8, "X"), + (0x9DC, "M", "ড়"), + (0x9DD, "M", "ঢ়"), + (0x9DE, "X"), + (0x9DF, "M", "য়"), + (0x9E0, "V"), + (0x9E4, "X"), + (0x9E6, "V"), + (0x9FF, "X"), + (0xA01, "V"), + (0xA04, "X"), + (0xA05, "V"), + (0xA0B, "X"), + (0xA0F, "V"), + (0xA11, "X"), + (0xA13, "V"), + (0xA29, "X"), + (0xA2A, "V"), + (0xA31, "X"), + (0xA32, "V"), + (0xA33, "M", "ਲ਼"), + (0xA34, "X"), + (0xA35, "V"), + (0xA36, "M", "ਸ਼"), + (0xA37, "X"), + (0xA38, "V"), + (0xA3A, "X"), + (0xA3C, "V"), + (0xA3D, "X"), + (0xA3E, "V"), + (0xA43, "X"), + (0xA47, "V"), + (0xA49, "X"), + (0xA4B, "V"), + (0xA4E, "X"), + (0xA51, "V"), + (0xA52, "X"), + (0xA59, "M", "ਖ਼"), + (0xA5A, "M", "ਗ਼"), + (0xA5B, "M", "ਜ਼"), + (0xA5C, "V"), + (0xA5D, "X"), + ] + + +def _seg_11() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xA5E, "M", "ਫ਼"), + (0xA5F, "X"), + (0xA66, "V"), + (0xA77, "X"), + (0xA81, "V"), + (0xA84, "X"), + (0xA85, "V"), + (0xA8E, "X"), + (0xA8F, "V"), + (0xA92, "X"), + (0xA93, "V"), + (0xAA9, "X"), + (0xAAA, "V"), + (0xAB1, "X"), + (0xAB2, "V"), + (0xAB4, "X"), + (0xAB5, "V"), + (0xABA, "X"), + (0xABC, "V"), + (0xAC6, "X"), + (0xAC7, "V"), + (0xACA, "X"), + (0xACB, "V"), + (0xACE, "X"), + (0xAD0, "V"), + (0xAD1, "X"), + (0xAE0, "V"), + (0xAE4, "X"), + (0xAE6, "V"), + (0xAF2, "X"), + (0xAF9, "V"), + (0xB00, "X"), + (0xB01, "V"), + (0xB04, "X"), + (0xB05, "V"), + (0xB0D, "X"), + (0xB0F, "V"), + (0xB11, "X"), + (0xB13, "V"), + (0xB29, "X"), + (0xB2A, "V"), + (0xB31, "X"), + (0xB32, "V"), + (0xB34, "X"), + (0xB35, "V"), + (0xB3A, "X"), + (0xB3C, "V"), + (0xB45, "X"), + (0xB47, "V"), + (0xB49, "X"), + (0xB4B, "V"), + (0xB4E, "X"), + (0xB55, "V"), + (0xB58, "X"), + (0xB5C, "M", "ଡ଼"), + (0xB5D, "M", "ଢ଼"), + (0xB5E, "X"), + (0xB5F, "V"), + (0xB64, "X"), + (0xB66, "V"), + (0xB78, "X"), + (0xB82, "V"), + (0xB84, "X"), + (0xB85, "V"), + (0xB8B, "X"), + (0xB8E, "V"), + (0xB91, "X"), + (0xB92, "V"), + (0xB96, "X"), + (0xB99, "V"), + (0xB9B, "X"), + (0xB9C, "V"), + (0xB9D, "X"), + (0xB9E, "V"), + (0xBA0, "X"), + (0xBA3, "V"), + (0xBA5, "X"), + (0xBA8, "V"), + (0xBAB, "X"), + (0xBAE, "V"), + (0xBBA, "X"), + (0xBBE, "V"), + (0xBC3, "X"), + (0xBC6, "V"), + (0xBC9, "X"), + (0xBCA, "V"), + (0xBCE, "X"), + (0xBD0, "V"), + (0xBD1, "X"), + (0xBD7, "V"), + (0xBD8, "X"), + (0xBE6, "V"), + (0xBFB, "X"), + (0xC00, "V"), + (0xC0D, "X"), + (0xC0E, "V"), + (0xC11, "X"), + (0xC12, "V"), + (0xC29, "X"), + (0xC2A, "V"), + ] + + +def _seg_12() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xC3A, "X"), + (0xC3C, "V"), + (0xC45, "X"), + (0xC46, "V"), + (0xC49, "X"), + (0xC4A, "V"), + (0xC4E, "X"), + (0xC55, "V"), + (0xC57, "X"), + (0xC58, "V"), + (0xC5B, "X"), + (0xC5D, "V"), + (0xC5E, "X"), + (0xC60, "V"), + (0xC64, "X"), + (0xC66, "V"), + (0xC70, "X"), + (0xC77, "V"), + (0xC8D, "X"), + (0xC8E, "V"), + (0xC91, "X"), + (0xC92, "V"), + (0xCA9, "X"), + (0xCAA, "V"), + (0xCB4, "X"), + (0xCB5, "V"), + (0xCBA, "X"), + (0xCBC, "V"), + (0xCC5, "X"), + (0xCC6, "V"), + (0xCC9, "X"), + (0xCCA, "V"), + (0xCCE, "X"), + (0xCD5, "V"), + (0xCD7, "X"), + (0xCDD, "V"), + (0xCDF, "X"), + (0xCE0, "V"), + (0xCE4, "X"), + (0xCE6, "V"), + (0xCF0, "X"), + (0xCF1, "V"), + (0xCF4, "X"), + (0xD00, "V"), + (0xD0D, "X"), + (0xD0E, "V"), + (0xD11, "X"), + (0xD12, "V"), + (0xD45, "X"), + (0xD46, "V"), + (0xD49, "X"), + (0xD4A, "V"), + (0xD50, "X"), + (0xD54, "V"), + (0xD64, "X"), + (0xD66, "V"), + (0xD80, "X"), + (0xD81, "V"), + (0xD84, "X"), + (0xD85, "V"), + (0xD97, "X"), + (0xD9A, "V"), + (0xDB2, "X"), + (0xDB3, "V"), + (0xDBC, "X"), + (0xDBD, "V"), + (0xDBE, "X"), + (0xDC0, "V"), + (0xDC7, "X"), + (0xDCA, "V"), + (0xDCB, "X"), + (0xDCF, "V"), + (0xDD5, "X"), + (0xDD6, "V"), + (0xDD7, "X"), + (0xDD8, "V"), + (0xDE0, "X"), + (0xDE6, "V"), + (0xDF0, "X"), + (0xDF2, "V"), + (0xDF5, "X"), + (0xE01, "V"), + (0xE33, "M", "ํา"), + (0xE34, "V"), + (0xE3B, "X"), + (0xE3F, "V"), + (0xE5C, "X"), + (0xE81, "V"), + (0xE83, "X"), + (0xE84, "V"), + (0xE85, "X"), + (0xE86, "V"), + (0xE8B, "X"), + (0xE8C, "V"), + (0xEA4, "X"), + (0xEA5, "V"), + (0xEA6, "X"), + (0xEA7, "V"), + (0xEB3, "M", "ໍາ"), + (0xEB4, "V"), + ] + + +def _seg_13() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xEBE, "X"), + (0xEC0, "V"), + (0xEC5, "X"), + (0xEC6, "V"), + (0xEC7, "X"), + (0xEC8, "V"), + (0xECF, "X"), + (0xED0, "V"), + (0xEDA, "X"), + (0xEDC, "M", "ຫນ"), + (0xEDD, "M", "ຫມ"), + (0xEDE, "V"), + (0xEE0, "X"), + (0xF00, "V"), + (0xF0C, "M", "་"), + (0xF0D, "V"), + (0xF43, "M", "གྷ"), + (0xF44, "V"), + (0xF48, "X"), + (0xF49, "V"), + (0xF4D, "M", "ཌྷ"), + (0xF4E, "V"), + (0xF52, "M", "དྷ"), + (0xF53, "V"), + (0xF57, "M", "བྷ"), + (0xF58, "V"), + (0xF5C, "M", "ཛྷ"), + (0xF5D, "V"), + (0xF69, "M", "ཀྵ"), + (0xF6A, "V"), + (0xF6D, "X"), + (0xF71, "V"), + (0xF73, "M", "ཱི"), + (0xF74, "V"), + (0xF75, "M", "ཱུ"), + (0xF76, "M", "ྲྀ"), + (0xF77, "M", "ྲཱྀ"), + (0xF78, "M", "ླྀ"), + (0xF79, "M", "ླཱྀ"), + (0xF7A, "V"), + (0xF81, "M", "ཱྀ"), + (0xF82, "V"), + (0xF93, "M", "ྒྷ"), + (0xF94, "V"), + (0xF98, "X"), + (0xF99, "V"), + (0xF9D, "M", "ྜྷ"), + (0xF9E, "V"), + (0xFA2, "M", "ྡྷ"), + (0xFA3, "V"), + (0xFA7, "M", "ྦྷ"), + (0xFA8, "V"), + (0xFAC, "M", "ྫྷ"), + (0xFAD, "V"), + (0xFB9, "M", "ྐྵ"), + (0xFBA, "V"), + (0xFBD, "X"), + (0xFBE, "V"), + (0xFCD, "X"), + (0xFCE, "V"), + (0xFDB, "X"), + (0x1000, "V"), + (0x10A0, "X"), + (0x10C7, "M", "ⴧ"), + (0x10C8, "X"), + (0x10CD, "M", "ⴭ"), + (0x10CE, "X"), + (0x10D0, "V"), + (0x10FC, "M", "ნ"), + (0x10FD, "V"), + (0x115F, "X"), + (0x1161, "V"), + (0x1249, "X"), + (0x124A, "V"), + (0x124E, "X"), + (0x1250, "V"), + (0x1257, "X"), + (0x1258, "V"), + (0x1259, "X"), + (0x125A, "V"), + (0x125E, "X"), + (0x1260, "V"), + (0x1289, "X"), + (0x128A, "V"), + (0x128E, "X"), + (0x1290, "V"), + (0x12B1, "X"), + (0x12B2, "V"), + (0x12B6, "X"), + (0x12B8, "V"), + (0x12BF, "X"), + (0x12C0, "V"), + (0x12C1, "X"), + (0x12C2, "V"), + (0x12C6, "X"), + (0x12C8, "V"), + (0x12D7, "X"), + (0x12D8, "V"), + (0x1311, "X"), + (0x1312, "V"), + ] + + +def _seg_14() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1316, "X"), + (0x1318, "V"), + (0x135B, "X"), + (0x135D, "V"), + (0x137D, "X"), + (0x1380, "V"), + (0x139A, "X"), + (0x13A0, "V"), + (0x13F6, "X"), + (0x13F8, "M", "Ᏸ"), + (0x13F9, "M", "Ᏹ"), + (0x13FA, "M", "Ᏺ"), + (0x13FB, "M", "Ᏻ"), + (0x13FC, "M", "Ᏼ"), + (0x13FD, "M", "Ᏽ"), + (0x13FE, "X"), + (0x1400, "V"), + (0x1680, "X"), + (0x1681, "V"), + (0x169D, "X"), + (0x16A0, "V"), + (0x16F9, "X"), + (0x1700, "V"), + (0x1716, "X"), + (0x171F, "V"), + (0x1737, "X"), + (0x1740, "V"), + (0x1754, "X"), + (0x1760, "V"), + (0x176D, "X"), + (0x176E, "V"), + (0x1771, "X"), + (0x1772, "V"), + (0x1774, "X"), + (0x1780, "V"), + (0x17B4, "X"), + (0x17B6, "V"), + (0x17DE, "X"), + (0x17E0, "V"), + (0x17EA, "X"), + (0x17F0, "V"), + (0x17FA, "X"), + (0x1800, "V"), + (0x1806, "X"), + (0x1807, "V"), + (0x180B, "I"), + (0x180E, "X"), + (0x180F, "I"), + (0x1810, "V"), + (0x181A, "X"), + (0x1820, "V"), + (0x1879, "X"), + (0x1880, "V"), + (0x18AB, "X"), + (0x18B0, "V"), + (0x18F6, "X"), + (0x1900, "V"), + (0x191F, "X"), + (0x1920, "V"), + (0x192C, "X"), + (0x1930, "V"), + (0x193C, "X"), + (0x1940, "V"), + (0x1941, "X"), + (0x1944, "V"), + (0x196E, "X"), + (0x1970, "V"), + (0x1975, "X"), + (0x1980, "V"), + (0x19AC, "X"), + (0x19B0, "V"), + (0x19CA, "X"), + (0x19D0, "V"), + (0x19DB, "X"), + (0x19DE, "V"), + (0x1A1C, "X"), + (0x1A1E, "V"), + (0x1A5F, "X"), + (0x1A60, "V"), + (0x1A7D, "X"), + (0x1A7F, "V"), + (0x1A8A, "X"), + (0x1A90, "V"), + (0x1A9A, "X"), + (0x1AA0, "V"), + (0x1AAE, "X"), + (0x1AB0, "V"), + (0x1ACF, "X"), + (0x1B00, "V"), + (0x1B4D, "X"), + (0x1B50, "V"), + (0x1B7F, "X"), + (0x1B80, "V"), + (0x1BF4, "X"), + (0x1BFC, "V"), + (0x1C38, "X"), + (0x1C3B, "V"), + (0x1C4A, "X"), + (0x1C4D, "V"), + (0x1C80, "M", "в"), + ] + + +def _seg_15() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1C81, "M", "д"), + (0x1C82, "M", "о"), + (0x1C83, "M", "с"), + (0x1C84, "M", "т"), + (0x1C86, "M", "ъ"), + (0x1C87, "M", "ѣ"), + (0x1C88, "M", "ꙋ"), + (0x1C89, "X"), + (0x1C90, "M", "ა"), + (0x1C91, "M", "ბ"), + (0x1C92, "M", "გ"), + (0x1C93, "M", "დ"), + (0x1C94, "M", "ე"), + (0x1C95, "M", "ვ"), + (0x1C96, "M", "ზ"), + (0x1C97, "M", "თ"), + (0x1C98, "M", "ი"), + (0x1C99, "M", "კ"), + (0x1C9A, "M", "ლ"), + (0x1C9B, "M", "მ"), + (0x1C9C, "M", "ნ"), + (0x1C9D, "M", "ო"), + (0x1C9E, "M", "პ"), + (0x1C9F, "M", "ჟ"), + (0x1CA0, "M", "რ"), + (0x1CA1, "M", "ს"), + (0x1CA2, "M", "ტ"), + (0x1CA3, "M", "უ"), + (0x1CA4, "M", "ფ"), + (0x1CA5, "M", "ქ"), + (0x1CA6, "M", "ღ"), + (0x1CA7, "M", "ყ"), + (0x1CA8, "M", "შ"), + (0x1CA9, "M", "ჩ"), + (0x1CAA, "M", "ც"), + (0x1CAB, "M", "ძ"), + (0x1CAC, "M", "წ"), + (0x1CAD, "M", "ჭ"), + (0x1CAE, "M", "ხ"), + (0x1CAF, "M", "ჯ"), + (0x1CB0, "M", "ჰ"), + (0x1CB1, "M", "ჱ"), + (0x1CB2, "M", "ჲ"), + (0x1CB3, "M", "ჳ"), + (0x1CB4, "M", "ჴ"), + (0x1CB5, "M", "ჵ"), + (0x1CB6, "M", "ჶ"), + (0x1CB7, "M", "ჷ"), + (0x1CB8, "M", "ჸ"), + (0x1CB9, "M", "ჹ"), + (0x1CBA, "M", "ჺ"), + (0x1CBB, "X"), + (0x1CBD, "M", "ჽ"), + (0x1CBE, "M", "ჾ"), + (0x1CBF, "M", "ჿ"), + (0x1CC0, "V"), + (0x1CC8, "X"), + (0x1CD0, "V"), + (0x1CFB, "X"), + (0x1D00, "V"), + (0x1D2C, "M", "a"), + (0x1D2D, "M", "æ"), + (0x1D2E, "M", "b"), + (0x1D2F, "V"), + (0x1D30, "M", "d"), + (0x1D31, "M", "e"), + (0x1D32, "M", "ǝ"), + (0x1D33, "M", "g"), + (0x1D34, "M", "h"), + (0x1D35, "M", "i"), + (0x1D36, "M", "j"), + (0x1D37, "M", "k"), + (0x1D38, "M", "l"), + (0x1D39, "M", "m"), + (0x1D3A, "M", "n"), + (0x1D3B, "V"), + (0x1D3C, "M", "o"), + (0x1D3D, "M", "ȣ"), + (0x1D3E, "M", "p"), + (0x1D3F, "M", "r"), + (0x1D40, "M", "t"), + (0x1D41, "M", "u"), + (0x1D42, "M", "w"), + (0x1D43, "M", "a"), + (0x1D44, "M", "ɐ"), + (0x1D45, "M", "ɑ"), + (0x1D46, "M", "ᴂ"), + (0x1D47, "M", "b"), + (0x1D48, "M", "d"), + (0x1D49, "M", "e"), + (0x1D4A, "M", "ə"), + (0x1D4B, "M", "ɛ"), + (0x1D4C, "M", "ɜ"), + (0x1D4D, "M", "g"), + (0x1D4E, "V"), + (0x1D4F, "M", "k"), + (0x1D50, "M", "m"), + (0x1D51, "M", "ŋ"), + (0x1D52, "M", "o"), + (0x1D53, "M", "ɔ"), + ] + + +def _seg_16() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1D54, "M", "ᴖ"), + (0x1D55, "M", "ᴗ"), + (0x1D56, "M", "p"), + (0x1D57, "M", "t"), + (0x1D58, "M", "u"), + (0x1D59, "M", "ᴝ"), + (0x1D5A, "M", "ɯ"), + (0x1D5B, "M", "v"), + (0x1D5C, "M", "ᴥ"), + (0x1D5D, "M", "β"), + (0x1D5E, "M", "γ"), + (0x1D5F, "M", "δ"), + (0x1D60, "M", "φ"), + (0x1D61, "M", "χ"), + (0x1D62, "M", "i"), + (0x1D63, "M", "r"), + (0x1D64, "M", "u"), + (0x1D65, "M", "v"), + (0x1D66, "M", "β"), + (0x1D67, "M", "γ"), + (0x1D68, "M", "ρ"), + (0x1D69, "M", "φ"), + (0x1D6A, "M", "χ"), + (0x1D6B, "V"), + (0x1D78, "M", "н"), + (0x1D79, "V"), + (0x1D9B, "M", "ɒ"), + (0x1D9C, "M", "c"), + (0x1D9D, "M", "ɕ"), + (0x1D9E, "M", "ð"), + (0x1D9F, "M", "ɜ"), + (0x1DA0, "M", "f"), + (0x1DA1, "M", "ɟ"), + (0x1DA2, "M", "ɡ"), + (0x1DA3, "M", "ɥ"), + (0x1DA4, "M", "ɨ"), + (0x1DA5, "M", "ɩ"), + (0x1DA6, "M", "ɪ"), + (0x1DA7, "M", "ᵻ"), + (0x1DA8, "M", "ʝ"), + (0x1DA9, "M", "ɭ"), + (0x1DAA, "M", "ᶅ"), + (0x1DAB, "M", "ʟ"), + (0x1DAC, "M", "ɱ"), + (0x1DAD, "M", "ɰ"), + (0x1DAE, "M", "ɲ"), + (0x1DAF, "M", "ɳ"), + (0x1DB0, "M", "ɴ"), + (0x1DB1, "M", "ɵ"), + (0x1DB2, "M", "ɸ"), + (0x1DB3, "M", "ʂ"), + (0x1DB4, "M", "ʃ"), + (0x1DB5, "M", "ƫ"), + (0x1DB6, "M", "ʉ"), + (0x1DB7, "M", "ʊ"), + (0x1DB8, "M", "ᴜ"), + (0x1DB9, "M", "ʋ"), + (0x1DBA, "M", "ʌ"), + (0x1DBB, "M", "z"), + (0x1DBC, "M", "ʐ"), + (0x1DBD, "M", "ʑ"), + (0x1DBE, "M", "ʒ"), + (0x1DBF, "M", "θ"), + (0x1DC0, "V"), + (0x1E00, "M", "ḁ"), + (0x1E01, "V"), + (0x1E02, "M", "ḃ"), + (0x1E03, "V"), + (0x1E04, "M", "ḅ"), + (0x1E05, "V"), + (0x1E06, "M", "ḇ"), + (0x1E07, "V"), + (0x1E08, "M", "ḉ"), + (0x1E09, "V"), + (0x1E0A, "M", "ḋ"), + (0x1E0B, "V"), + (0x1E0C, "M", "ḍ"), + (0x1E0D, "V"), + (0x1E0E, "M", "ḏ"), + (0x1E0F, "V"), + (0x1E10, "M", "ḑ"), + (0x1E11, "V"), + (0x1E12, "M", "ḓ"), + (0x1E13, "V"), + (0x1E14, "M", "ḕ"), + (0x1E15, "V"), + (0x1E16, "M", "ḗ"), + (0x1E17, "V"), + (0x1E18, "M", "ḙ"), + (0x1E19, "V"), + (0x1E1A, "M", "ḛ"), + (0x1E1B, "V"), + (0x1E1C, "M", "ḝ"), + (0x1E1D, "V"), + (0x1E1E, "M", "ḟ"), + (0x1E1F, "V"), + (0x1E20, "M", "ḡ"), + (0x1E21, "V"), + (0x1E22, "M", "ḣ"), + (0x1E23, "V"), + ] + + +def _seg_17() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1E24, "M", "ḥ"), + (0x1E25, "V"), + (0x1E26, "M", "ḧ"), + (0x1E27, "V"), + (0x1E28, "M", "ḩ"), + (0x1E29, "V"), + (0x1E2A, "M", "ḫ"), + (0x1E2B, "V"), + (0x1E2C, "M", "ḭ"), + (0x1E2D, "V"), + (0x1E2E, "M", "ḯ"), + (0x1E2F, "V"), + (0x1E30, "M", "ḱ"), + (0x1E31, "V"), + (0x1E32, "M", "ḳ"), + (0x1E33, "V"), + (0x1E34, "M", "ḵ"), + (0x1E35, "V"), + (0x1E36, "M", "ḷ"), + (0x1E37, "V"), + (0x1E38, "M", "ḹ"), + (0x1E39, "V"), + (0x1E3A, "M", "ḻ"), + (0x1E3B, "V"), + (0x1E3C, "M", "ḽ"), + (0x1E3D, "V"), + (0x1E3E, "M", "ḿ"), + (0x1E3F, "V"), + (0x1E40, "M", "ṁ"), + (0x1E41, "V"), + (0x1E42, "M", "ṃ"), + (0x1E43, "V"), + (0x1E44, "M", "ṅ"), + (0x1E45, "V"), + (0x1E46, "M", "ṇ"), + (0x1E47, "V"), + (0x1E48, "M", "ṉ"), + (0x1E49, "V"), + (0x1E4A, "M", "ṋ"), + (0x1E4B, "V"), + (0x1E4C, "M", "ṍ"), + (0x1E4D, "V"), + (0x1E4E, "M", "ṏ"), + (0x1E4F, "V"), + (0x1E50, "M", "ṑ"), + (0x1E51, "V"), + (0x1E52, "M", "ṓ"), + (0x1E53, "V"), + (0x1E54, "M", "ṕ"), + (0x1E55, "V"), + (0x1E56, "M", "ṗ"), + (0x1E57, "V"), + (0x1E58, "M", "ṙ"), + (0x1E59, "V"), + (0x1E5A, "M", "ṛ"), + (0x1E5B, "V"), + (0x1E5C, "M", "ṝ"), + (0x1E5D, "V"), + (0x1E5E, "M", "ṟ"), + (0x1E5F, "V"), + (0x1E60, "M", "ṡ"), + (0x1E61, "V"), + (0x1E62, "M", "ṣ"), + (0x1E63, "V"), + (0x1E64, "M", "ṥ"), + (0x1E65, "V"), + (0x1E66, "M", "ṧ"), + (0x1E67, "V"), + (0x1E68, "M", "ṩ"), + (0x1E69, "V"), + (0x1E6A, "M", "ṫ"), + (0x1E6B, "V"), + (0x1E6C, "M", "ṭ"), + (0x1E6D, "V"), + (0x1E6E, "M", "ṯ"), + (0x1E6F, "V"), + (0x1E70, "M", "ṱ"), + (0x1E71, "V"), + (0x1E72, "M", "ṳ"), + (0x1E73, "V"), + (0x1E74, "M", "ṵ"), + (0x1E75, "V"), + (0x1E76, "M", "ṷ"), + (0x1E77, "V"), + (0x1E78, "M", "ṹ"), + (0x1E79, "V"), + (0x1E7A, "M", "ṻ"), + (0x1E7B, "V"), + (0x1E7C, "M", "ṽ"), + (0x1E7D, "V"), + (0x1E7E, "M", "ṿ"), + (0x1E7F, "V"), + (0x1E80, "M", "ẁ"), + (0x1E81, "V"), + (0x1E82, "M", "ẃ"), + (0x1E83, "V"), + (0x1E84, "M", "ẅ"), + (0x1E85, "V"), + (0x1E86, "M", "ẇ"), + (0x1E87, "V"), + ] + + +def _seg_18() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1E88, "M", "ẉ"), + (0x1E89, "V"), + (0x1E8A, "M", "ẋ"), + (0x1E8B, "V"), + (0x1E8C, "M", "ẍ"), + (0x1E8D, "V"), + (0x1E8E, "M", "ẏ"), + (0x1E8F, "V"), + (0x1E90, "M", "ẑ"), + (0x1E91, "V"), + (0x1E92, "M", "ẓ"), + (0x1E93, "V"), + (0x1E94, "M", "ẕ"), + (0x1E95, "V"), + (0x1E9A, "M", "aʾ"), + (0x1E9B, "M", "ṡ"), + (0x1E9C, "V"), + (0x1E9E, "M", "ß"), + (0x1E9F, "V"), + (0x1EA0, "M", "ạ"), + (0x1EA1, "V"), + (0x1EA2, "M", "ả"), + (0x1EA3, "V"), + (0x1EA4, "M", "ấ"), + (0x1EA5, "V"), + (0x1EA6, "M", "ầ"), + (0x1EA7, "V"), + (0x1EA8, "M", "ẩ"), + (0x1EA9, "V"), + (0x1EAA, "M", "ẫ"), + (0x1EAB, "V"), + (0x1EAC, "M", "ậ"), + (0x1EAD, "V"), + (0x1EAE, "M", "ắ"), + (0x1EAF, "V"), + (0x1EB0, "M", "ằ"), + (0x1EB1, "V"), + (0x1EB2, "M", "ẳ"), + (0x1EB3, "V"), + (0x1EB4, "M", "ẵ"), + (0x1EB5, "V"), + (0x1EB6, "M", "ặ"), + (0x1EB7, "V"), + (0x1EB8, "M", "ẹ"), + (0x1EB9, "V"), + (0x1EBA, "M", "ẻ"), + (0x1EBB, "V"), + (0x1EBC, "M", "ẽ"), + (0x1EBD, "V"), + (0x1EBE, "M", "ế"), + (0x1EBF, "V"), + (0x1EC0, "M", "ề"), + (0x1EC1, "V"), + (0x1EC2, "M", "ể"), + (0x1EC3, "V"), + (0x1EC4, "M", "ễ"), + (0x1EC5, "V"), + (0x1EC6, "M", "ệ"), + (0x1EC7, "V"), + (0x1EC8, "M", "ỉ"), + (0x1EC9, "V"), + (0x1ECA, "M", "ị"), + (0x1ECB, "V"), + (0x1ECC, "M", "ọ"), + (0x1ECD, "V"), + (0x1ECE, "M", "ỏ"), + (0x1ECF, "V"), + (0x1ED0, "M", "ố"), + (0x1ED1, "V"), + (0x1ED2, "M", "ồ"), + (0x1ED3, "V"), + (0x1ED4, "M", "ổ"), + (0x1ED5, "V"), + (0x1ED6, "M", "ỗ"), + (0x1ED7, "V"), + (0x1ED8, "M", "ộ"), + (0x1ED9, "V"), + (0x1EDA, "M", "ớ"), + (0x1EDB, "V"), + (0x1EDC, "M", "ờ"), + (0x1EDD, "V"), + (0x1EDE, "M", "ở"), + (0x1EDF, "V"), + (0x1EE0, "M", "ỡ"), + (0x1EE1, "V"), + (0x1EE2, "M", "ợ"), + (0x1EE3, "V"), + (0x1EE4, "M", "ụ"), + (0x1EE5, "V"), + (0x1EE6, "M", "ủ"), + (0x1EE7, "V"), + (0x1EE8, "M", "ứ"), + (0x1EE9, "V"), + (0x1EEA, "M", "ừ"), + (0x1EEB, "V"), + (0x1EEC, "M", "ử"), + (0x1EED, "V"), + (0x1EEE, "M", "ữ"), + (0x1EEF, "V"), + (0x1EF0, "M", "ự"), + ] + + +def _seg_19() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1EF1, "V"), + (0x1EF2, "M", "ỳ"), + (0x1EF3, "V"), + (0x1EF4, "M", "ỵ"), + (0x1EF5, "V"), + (0x1EF6, "M", "ỷ"), + (0x1EF7, "V"), + (0x1EF8, "M", "ỹ"), + (0x1EF9, "V"), + (0x1EFA, "M", "ỻ"), + (0x1EFB, "V"), + (0x1EFC, "M", "ỽ"), + (0x1EFD, "V"), + (0x1EFE, "M", "ỿ"), + (0x1EFF, "V"), + (0x1F08, "M", "ἀ"), + (0x1F09, "M", "ἁ"), + (0x1F0A, "M", "ἂ"), + (0x1F0B, "M", "ἃ"), + (0x1F0C, "M", "ἄ"), + (0x1F0D, "M", "ἅ"), + (0x1F0E, "M", "ἆ"), + (0x1F0F, "M", "ἇ"), + (0x1F10, "V"), + (0x1F16, "X"), + (0x1F18, "M", "ἐ"), + (0x1F19, "M", "ἑ"), + (0x1F1A, "M", "ἒ"), + (0x1F1B, "M", "ἓ"), + (0x1F1C, "M", "ἔ"), + (0x1F1D, "M", "ἕ"), + (0x1F1E, "X"), + (0x1F20, "V"), + (0x1F28, "M", "ἠ"), + (0x1F29, "M", "ἡ"), + (0x1F2A, "M", "ἢ"), + (0x1F2B, "M", "ἣ"), + (0x1F2C, "M", "ἤ"), + (0x1F2D, "M", "ἥ"), + (0x1F2E, "M", "ἦ"), + (0x1F2F, "M", "ἧ"), + (0x1F30, "V"), + (0x1F38, "M", "ἰ"), + (0x1F39, "M", "ἱ"), + (0x1F3A, "M", "ἲ"), + (0x1F3B, "M", "ἳ"), + (0x1F3C, "M", "ἴ"), + (0x1F3D, "M", "ἵ"), + (0x1F3E, "M", "ἶ"), + (0x1F3F, "M", "ἷ"), + (0x1F40, "V"), + (0x1F46, "X"), + (0x1F48, "M", "ὀ"), + (0x1F49, "M", "ὁ"), + (0x1F4A, "M", "ὂ"), + (0x1F4B, "M", "ὃ"), + (0x1F4C, "M", "ὄ"), + (0x1F4D, "M", "ὅ"), + (0x1F4E, "X"), + (0x1F50, "V"), + (0x1F58, "X"), + (0x1F59, "M", "ὑ"), + (0x1F5A, "X"), + (0x1F5B, "M", "ὓ"), + (0x1F5C, "X"), + (0x1F5D, "M", "ὕ"), + (0x1F5E, "X"), + (0x1F5F, "M", "ὗ"), + (0x1F60, "V"), + (0x1F68, "M", "ὠ"), + (0x1F69, "M", "ὡ"), + (0x1F6A, "M", "ὢ"), + (0x1F6B, "M", "ὣ"), + (0x1F6C, "M", "ὤ"), + (0x1F6D, "M", "ὥ"), + (0x1F6E, "M", "ὦ"), + (0x1F6F, "M", "ὧ"), + (0x1F70, "V"), + (0x1F71, "M", "ά"), + (0x1F72, "V"), + (0x1F73, "M", "έ"), + (0x1F74, "V"), + (0x1F75, "M", "ή"), + (0x1F76, "V"), + (0x1F77, "M", "ί"), + (0x1F78, "V"), + (0x1F79, "M", "ό"), + (0x1F7A, "V"), + (0x1F7B, "M", "ύ"), + (0x1F7C, "V"), + (0x1F7D, "M", "ώ"), + (0x1F7E, "X"), + (0x1F80, "M", "ἀι"), + (0x1F81, "M", "ἁι"), + (0x1F82, "M", "ἂι"), + (0x1F83, "M", "ἃι"), + (0x1F84, "M", "ἄι"), + (0x1F85, "M", "ἅι"), + (0x1F86, "M", "ἆι"), + (0x1F87, "M", "ἇι"), + ] + + +def _seg_20() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1F88, "M", "ἀι"), + (0x1F89, "M", "ἁι"), + (0x1F8A, "M", "ἂι"), + (0x1F8B, "M", "ἃι"), + (0x1F8C, "M", "ἄι"), + (0x1F8D, "M", "ἅι"), + (0x1F8E, "M", "ἆι"), + (0x1F8F, "M", "ἇι"), + (0x1F90, "M", "ἠι"), + (0x1F91, "M", "ἡι"), + (0x1F92, "M", "ἢι"), + (0x1F93, "M", "ἣι"), + (0x1F94, "M", "ἤι"), + (0x1F95, "M", "ἥι"), + (0x1F96, "M", "ἦι"), + (0x1F97, "M", "ἧι"), + (0x1F98, "M", "ἠι"), + (0x1F99, "M", "ἡι"), + (0x1F9A, "M", "ἢι"), + (0x1F9B, "M", "ἣι"), + (0x1F9C, "M", "ἤι"), + (0x1F9D, "M", "ἥι"), + (0x1F9E, "M", "ἦι"), + (0x1F9F, "M", "ἧι"), + (0x1FA0, "M", "ὠι"), + (0x1FA1, "M", "ὡι"), + (0x1FA2, "M", "ὢι"), + (0x1FA3, "M", "ὣι"), + (0x1FA4, "M", "ὤι"), + (0x1FA5, "M", "ὥι"), + (0x1FA6, "M", "ὦι"), + (0x1FA7, "M", "ὧι"), + (0x1FA8, "M", "ὠι"), + (0x1FA9, "M", "ὡι"), + (0x1FAA, "M", "ὢι"), + (0x1FAB, "M", "ὣι"), + (0x1FAC, "M", "ὤι"), + (0x1FAD, "M", "ὥι"), + (0x1FAE, "M", "ὦι"), + (0x1FAF, "M", "ὧι"), + (0x1FB0, "V"), + (0x1FB2, "M", "ὰι"), + (0x1FB3, "M", "αι"), + (0x1FB4, "M", "άι"), + (0x1FB5, "X"), + (0x1FB6, "V"), + (0x1FB7, "M", "ᾶι"), + (0x1FB8, "M", "ᾰ"), + (0x1FB9, "M", "ᾱ"), + (0x1FBA, "M", "ὰ"), + (0x1FBB, "M", "ά"), + (0x1FBC, "M", "αι"), + (0x1FBD, "3", " ̓"), + (0x1FBE, "M", "ι"), + (0x1FBF, "3", " ̓"), + (0x1FC0, "3", " ͂"), + (0x1FC1, "3", " ̈͂"), + (0x1FC2, "M", "ὴι"), + (0x1FC3, "M", "ηι"), + (0x1FC4, "M", "ήι"), + (0x1FC5, "X"), + (0x1FC6, "V"), + (0x1FC7, "M", "ῆι"), + (0x1FC8, "M", "ὲ"), + (0x1FC9, "M", "έ"), + (0x1FCA, "M", "ὴ"), + (0x1FCB, "M", "ή"), + (0x1FCC, "M", "ηι"), + (0x1FCD, "3", " ̓̀"), + (0x1FCE, "3", " ̓́"), + (0x1FCF, "3", " ̓͂"), + (0x1FD0, "V"), + (0x1FD3, "M", "ΐ"), + (0x1FD4, "X"), + (0x1FD6, "V"), + (0x1FD8, "M", "ῐ"), + (0x1FD9, "M", "ῑ"), + (0x1FDA, "M", "ὶ"), + (0x1FDB, "M", "ί"), + (0x1FDC, "X"), + (0x1FDD, "3", " ̔̀"), + (0x1FDE, "3", " ̔́"), + (0x1FDF, "3", " ̔͂"), + (0x1FE0, "V"), + (0x1FE3, "M", "ΰ"), + (0x1FE4, "V"), + (0x1FE8, "M", "ῠ"), + (0x1FE9, "M", "ῡ"), + (0x1FEA, "M", "ὺ"), + (0x1FEB, "M", "ύ"), + (0x1FEC, "M", "ῥ"), + (0x1FED, "3", " ̈̀"), + (0x1FEE, "3", " ̈́"), + (0x1FEF, "3", "`"), + (0x1FF0, "X"), + (0x1FF2, "M", "ὼι"), + (0x1FF3, "M", "ωι"), + (0x1FF4, "M", "ώι"), + (0x1FF5, "X"), + (0x1FF6, "V"), + ] + + +def _seg_21() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1FF7, "M", "ῶι"), + (0x1FF8, "M", "ὸ"), + (0x1FF9, "M", "ό"), + (0x1FFA, "M", "ὼ"), + (0x1FFB, "M", "ώ"), + (0x1FFC, "M", "ωι"), + (0x1FFD, "3", " ́"), + (0x1FFE, "3", " ̔"), + (0x1FFF, "X"), + (0x2000, "3", " "), + (0x200B, "I"), + (0x200C, "D", ""), + (0x200E, "X"), + (0x2010, "V"), + (0x2011, "M", "‐"), + (0x2012, "V"), + (0x2017, "3", " ̳"), + (0x2018, "V"), + (0x2024, "X"), + (0x2027, "V"), + (0x2028, "X"), + (0x202F, "3", " "), + (0x2030, "V"), + (0x2033, "M", "′′"), + (0x2034, "M", "′′′"), + (0x2035, "V"), + (0x2036, "M", "‵‵"), + (0x2037, "M", "‵‵‵"), + (0x2038, "V"), + (0x203C, "3", "!!"), + (0x203D, "V"), + (0x203E, "3", " ̅"), + (0x203F, "V"), + (0x2047, "3", "??"), + (0x2048, "3", "?!"), + (0x2049, "3", "!?"), + (0x204A, "V"), + (0x2057, "M", "′′′′"), + (0x2058, "V"), + (0x205F, "3", " "), + (0x2060, "I"), + (0x2061, "X"), + (0x2064, "I"), + (0x2065, "X"), + (0x2070, "M", "0"), + (0x2071, "M", "i"), + (0x2072, "X"), + (0x2074, "M", "4"), + (0x2075, "M", "5"), + (0x2076, "M", "6"), + (0x2077, "M", "7"), + (0x2078, "M", "8"), + (0x2079, "M", "9"), + (0x207A, "3", "+"), + (0x207B, "M", "−"), + (0x207C, "3", "="), + (0x207D, "3", "("), + (0x207E, "3", ")"), + (0x207F, "M", "n"), + (0x2080, "M", "0"), + (0x2081, "M", "1"), + (0x2082, "M", "2"), + (0x2083, "M", "3"), + (0x2084, "M", "4"), + (0x2085, "M", "5"), + (0x2086, "M", "6"), + (0x2087, "M", "7"), + (0x2088, "M", "8"), + (0x2089, "M", "9"), + (0x208A, "3", "+"), + (0x208B, "M", "−"), + (0x208C, "3", "="), + (0x208D, "3", "("), + (0x208E, "3", ")"), + (0x208F, "X"), + (0x2090, "M", "a"), + (0x2091, "M", "e"), + (0x2092, "M", "o"), + (0x2093, "M", "x"), + (0x2094, "M", "ə"), + (0x2095, "M", "h"), + (0x2096, "M", "k"), + (0x2097, "M", "l"), + (0x2098, "M", "m"), + (0x2099, "M", "n"), + (0x209A, "M", "p"), + (0x209B, "M", "s"), + (0x209C, "M", "t"), + (0x209D, "X"), + (0x20A0, "V"), + (0x20A8, "M", "rs"), + (0x20A9, "V"), + (0x20C1, "X"), + (0x20D0, "V"), + (0x20F1, "X"), + (0x2100, "3", "a/c"), + (0x2101, "3", "a/s"), + (0x2102, "M", "c"), + (0x2103, "M", "°c"), + (0x2104, "V"), + ] + + +def _seg_22() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x2105, "3", "c/o"), + (0x2106, "3", "c/u"), + (0x2107, "M", "ɛ"), + (0x2108, "V"), + (0x2109, "M", "°f"), + (0x210A, "M", "g"), + (0x210B, "M", "h"), + (0x210F, "M", "ħ"), + (0x2110, "M", "i"), + (0x2112, "M", "l"), + (0x2114, "V"), + (0x2115, "M", "n"), + (0x2116, "M", "no"), + (0x2117, "V"), + (0x2119, "M", "p"), + (0x211A, "M", "q"), + (0x211B, "M", "r"), + (0x211E, "V"), + (0x2120, "M", "sm"), + (0x2121, "M", "tel"), + (0x2122, "M", "tm"), + (0x2123, "V"), + (0x2124, "M", "z"), + (0x2125, "V"), + (0x2126, "M", "ω"), + (0x2127, "V"), + (0x2128, "M", "z"), + (0x2129, "V"), + (0x212A, "M", "k"), + (0x212B, "M", "å"), + (0x212C, "M", "b"), + (0x212D, "M", "c"), + (0x212E, "V"), + (0x212F, "M", "e"), + (0x2131, "M", "f"), + (0x2132, "X"), + (0x2133, "M", "m"), + (0x2134, "M", "o"), + (0x2135, "M", "א"), + (0x2136, "M", "ב"), + (0x2137, "M", "ג"), + (0x2138, "M", "ד"), + (0x2139, "M", "i"), + (0x213A, "V"), + (0x213B, "M", "fax"), + (0x213C, "M", "π"), + (0x213D, "M", "γ"), + (0x213F, "M", "π"), + (0x2140, "M", "∑"), + (0x2141, "V"), + (0x2145, "M", "d"), + (0x2147, "M", "e"), + (0x2148, "M", "i"), + (0x2149, "M", "j"), + (0x214A, "V"), + (0x2150, "M", "1⁄7"), + (0x2151, "M", "1⁄9"), + (0x2152, "M", "1⁄10"), + (0x2153, "M", "1⁄3"), + (0x2154, "M", "2⁄3"), + (0x2155, "M", "1⁄5"), + (0x2156, "M", "2⁄5"), + (0x2157, "M", "3⁄5"), + (0x2158, "M", "4⁄5"), + (0x2159, "M", "1⁄6"), + (0x215A, "M", "5⁄6"), + (0x215B, "M", "1⁄8"), + (0x215C, "M", "3⁄8"), + (0x215D, "M", "5⁄8"), + (0x215E, "M", "7⁄8"), + (0x215F, "M", "1⁄"), + (0x2160, "M", "i"), + (0x2161, "M", "ii"), + (0x2162, "M", "iii"), + (0x2163, "M", "iv"), + (0x2164, "M", "v"), + (0x2165, "M", "vi"), + (0x2166, "M", "vii"), + (0x2167, "M", "viii"), + (0x2168, "M", "ix"), + (0x2169, "M", "x"), + (0x216A, "M", "xi"), + (0x216B, "M", "xii"), + (0x216C, "M", "l"), + (0x216D, "M", "c"), + (0x216E, "M", "d"), + (0x216F, "M", "m"), + (0x2170, "M", "i"), + (0x2171, "M", "ii"), + (0x2172, "M", "iii"), + (0x2173, "M", "iv"), + (0x2174, "M", "v"), + (0x2175, "M", "vi"), + (0x2176, "M", "vii"), + (0x2177, "M", "viii"), + (0x2178, "M", "ix"), + (0x2179, "M", "x"), + (0x217A, "M", "xi"), + (0x217B, "M", "xii"), + (0x217C, "M", "l"), + ] + + +def _seg_23() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x217D, "M", "c"), + (0x217E, "M", "d"), + (0x217F, "M", "m"), + (0x2180, "V"), + (0x2183, "X"), + (0x2184, "V"), + (0x2189, "M", "0⁄3"), + (0x218A, "V"), + (0x218C, "X"), + (0x2190, "V"), + (0x222C, "M", "∫∫"), + (0x222D, "M", "∫∫∫"), + (0x222E, "V"), + (0x222F, "M", "∮∮"), + (0x2230, "M", "∮∮∮"), + (0x2231, "V"), + (0x2329, "M", "〈"), + (0x232A, "M", "〉"), + (0x232B, "V"), + (0x2427, "X"), + (0x2440, "V"), + (0x244B, "X"), + (0x2460, "M", "1"), + (0x2461, "M", "2"), + (0x2462, "M", "3"), + (0x2463, "M", "4"), + (0x2464, "M", "5"), + (0x2465, "M", "6"), + (0x2466, "M", "7"), + (0x2467, "M", "8"), + (0x2468, "M", "9"), + (0x2469, "M", "10"), + (0x246A, "M", "11"), + (0x246B, "M", "12"), + (0x246C, "M", "13"), + (0x246D, "M", "14"), + (0x246E, "M", "15"), + (0x246F, "M", "16"), + (0x2470, "M", "17"), + (0x2471, "M", "18"), + (0x2472, "M", "19"), + (0x2473, "M", "20"), + (0x2474, "3", "(1)"), + (0x2475, "3", "(2)"), + (0x2476, "3", "(3)"), + (0x2477, "3", "(4)"), + (0x2478, "3", "(5)"), + (0x2479, "3", "(6)"), + (0x247A, "3", "(7)"), + (0x247B, "3", "(8)"), + (0x247C, "3", "(9)"), + (0x247D, "3", "(10)"), + (0x247E, "3", "(11)"), + (0x247F, "3", "(12)"), + (0x2480, "3", "(13)"), + (0x2481, "3", "(14)"), + (0x2482, "3", "(15)"), + (0x2483, "3", "(16)"), + (0x2484, "3", "(17)"), + (0x2485, "3", "(18)"), + (0x2486, "3", "(19)"), + (0x2487, "3", "(20)"), + (0x2488, "X"), + (0x249C, "3", "(a)"), + (0x249D, "3", "(b)"), + (0x249E, "3", "(c)"), + (0x249F, "3", "(d)"), + (0x24A0, "3", "(e)"), + (0x24A1, "3", "(f)"), + (0x24A2, "3", "(g)"), + (0x24A3, "3", "(h)"), + (0x24A4, "3", "(i)"), + (0x24A5, "3", "(j)"), + (0x24A6, "3", "(k)"), + (0x24A7, "3", "(l)"), + (0x24A8, "3", "(m)"), + (0x24A9, "3", "(n)"), + (0x24AA, "3", "(o)"), + (0x24AB, "3", "(p)"), + (0x24AC, "3", "(q)"), + (0x24AD, "3", "(r)"), + (0x24AE, "3", "(s)"), + (0x24AF, "3", "(t)"), + (0x24B0, "3", "(u)"), + (0x24B1, "3", "(v)"), + (0x24B2, "3", "(w)"), + (0x24B3, "3", "(x)"), + (0x24B4, "3", "(y)"), + (0x24B5, "3", "(z)"), + (0x24B6, "M", "a"), + (0x24B7, "M", "b"), + (0x24B8, "M", "c"), + (0x24B9, "M", "d"), + (0x24BA, "M", "e"), + (0x24BB, "M", "f"), + (0x24BC, "M", "g"), + (0x24BD, "M", "h"), + (0x24BE, "M", "i"), + (0x24BF, "M", "j"), + (0x24C0, "M", "k"), + ] + + +def _seg_24() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x24C1, "M", "l"), + (0x24C2, "M", "m"), + (0x24C3, "M", "n"), + (0x24C4, "M", "o"), + (0x24C5, "M", "p"), + (0x24C6, "M", "q"), + (0x24C7, "M", "r"), + (0x24C8, "M", "s"), + (0x24C9, "M", "t"), + (0x24CA, "M", "u"), + (0x24CB, "M", "v"), + (0x24CC, "M", "w"), + (0x24CD, "M", "x"), + (0x24CE, "M", "y"), + (0x24CF, "M", "z"), + (0x24D0, "M", "a"), + (0x24D1, "M", "b"), + (0x24D2, "M", "c"), + (0x24D3, "M", "d"), + (0x24D4, "M", "e"), + (0x24D5, "M", "f"), + (0x24D6, "M", "g"), + (0x24D7, "M", "h"), + (0x24D8, "M", "i"), + (0x24D9, "M", "j"), + (0x24DA, "M", "k"), + (0x24DB, "M", "l"), + (0x24DC, "M", "m"), + (0x24DD, "M", "n"), + (0x24DE, "M", "o"), + (0x24DF, "M", "p"), + (0x24E0, "M", "q"), + (0x24E1, "M", "r"), + (0x24E2, "M", "s"), + (0x24E3, "M", "t"), + (0x24E4, "M", "u"), + (0x24E5, "M", "v"), + (0x24E6, "M", "w"), + (0x24E7, "M", "x"), + (0x24E8, "M", "y"), + (0x24E9, "M", "z"), + (0x24EA, "M", "0"), + (0x24EB, "V"), + (0x2A0C, "M", "∫∫∫∫"), + (0x2A0D, "V"), + (0x2A74, "3", "::="), + (0x2A75, "3", "=="), + (0x2A76, "3", "==="), + (0x2A77, "V"), + (0x2ADC, "M", "⫝̸"), + (0x2ADD, "V"), + (0x2B74, "X"), + (0x2B76, "V"), + (0x2B96, "X"), + (0x2B97, "V"), + (0x2C00, "M", "ⰰ"), + (0x2C01, "M", "ⰱ"), + (0x2C02, "M", "ⰲ"), + (0x2C03, "M", "ⰳ"), + (0x2C04, "M", "ⰴ"), + (0x2C05, "M", "ⰵ"), + (0x2C06, "M", "ⰶ"), + (0x2C07, "M", "ⰷ"), + (0x2C08, "M", "ⰸ"), + (0x2C09, "M", "ⰹ"), + (0x2C0A, "M", "ⰺ"), + (0x2C0B, "M", "ⰻ"), + (0x2C0C, "M", "ⰼ"), + (0x2C0D, "M", "ⰽ"), + (0x2C0E, "M", "ⰾ"), + (0x2C0F, "M", "ⰿ"), + (0x2C10, "M", "ⱀ"), + (0x2C11, "M", "ⱁ"), + (0x2C12, "M", "ⱂ"), + (0x2C13, "M", "ⱃ"), + (0x2C14, "M", "ⱄ"), + (0x2C15, "M", "ⱅ"), + (0x2C16, "M", "ⱆ"), + (0x2C17, "M", "ⱇ"), + (0x2C18, "M", "ⱈ"), + (0x2C19, "M", "ⱉ"), + (0x2C1A, "M", "ⱊ"), + (0x2C1B, "M", "ⱋ"), + (0x2C1C, "M", "ⱌ"), + (0x2C1D, "M", "ⱍ"), + (0x2C1E, "M", "ⱎ"), + (0x2C1F, "M", "ⱏ"), + (0x2C20, "M", "ⱐ"), + (0x2C21, "M", "ⱑ"), + (0x2C22, "M", "ⱒ"), + (0x2C23, "M", "ⱓ"), + (0x2C24, "M", "ⱔ"), + (0x2C25, "M", "ⱕ"), + (0x2C26, "M", "ⱖ"), + (0x2C27, "M", "ⱗ"), + (0x2C28, "M", "ⱘ"), + (0x2C29, "M", "ⱙ"), + (0x2C2A, "M", "ⱚ"), + (0x2C2B, "M", "ⱛ"), + (0x2C2C, "M", "ⱜ"), + ] + + +def _seg_25() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x2C2D, "M", "ⱝ"), + (0x2C2E, "M", "ⱞ"), + (0x2C2F, "M", "ⱟ"), + (0x2C30, "V"), + (0x2C60, "M", "ⱡ"), + (0x2C61, "V"), + (0x2C62, "M", "ɫ"), + (0x2C63, "M", "ᵽ"), + (0x2C64, "M", "ɽ"), + (0x2C65, "V"), + (0x2C67, "M", "ⱨ"), + (0x2C68, "V"), + (0x2C69, "M", "ⱪ"), + (0x2C6A, "V"), + (0x2C6B, "M", "ⱬ"), + (0x2C6C, "V"), + (0x2C6D, "M", "ɑ"), + (0x2C6E, "M", "ɱ"), + (0x2C6F, "M", "ɐ"), + (0x2C70, "M", "ɒ"), + (0x2C71, "V"), + (0x2C72, "M", "ⱳ"), + (0x2C73, "V"), + (0x2C75, "M", "ⱶ"), + (0x2C76, "V"), + (0x2C7C, "M", "j"), + (0x2C7D, "M", "v"), + (0x2C7E, "M", "ȿ"), + (0x2C7F, "M", "ɀ"), + (0x2C80, "M", "ⲁ"), + (0x2C81, "V"), + (0x2C82, "M", "ⲃ"), + (0x2C83, "V"), + (0x2C84, "M", "ⲅ"), + (0x2C85, "V"), + (0x2C86, "M", "ⲇ"), + (0x2C87, "V"), + (0x2C88, "M", "ⲉ"), + (0x2C89, "V"), + (0x2C8A, "M", "ⲋ"), + (0x2C8B, "V"), + (0x2C8C, "M", "ⲍ"), + (0x2C8D, "V"), + (0x2C8E, "M", "ⲏ"), + (0x2C8F, "V"), + (0x2C90, "M", "ⲑ"), + (0x2C91, "V"), + (0x2C92, "M", "ⲓ"), + (0x2C93, "V"), + (0x2C94, "M", "ⲕ"), + (0x2C95, "V"), + (0x2C96, "M", "ⲗ"), + (0x2C97, "V"), + (0x2C98, "M", "ⲙ"), + (0x2C99, "V"), + (0x2C9A, "M", "ⲛ"), + (0x2C9B, "V"), + (0x2C9C, "M", "ⲝ"), + (0x2C9D, "V"), + (0x2C9E, "M", "ⲟ"), + (0x2C9F, "V"), + (0x2CA0, "M", "ⲡ"), + (0x2CA1, "V"), + (0x2CA2, "M", "ⲣ"), + (0x2CA3, "V"), + (0x2CA4, "M", "ⲥ"), + (0x2CA5, "V"), + (0x2CA6, "M", "ⲧ"), + (0x2CA7, "V"), + (0x2CA8, "M", "ⲩ"), + (0x2CA9, "V"), + (0x2CAA, "M", "ⲫ"), + (0x2CAB, "V"), + (0x2CAC, "M", "ⲭ"), + (0x2CAD, "V"), + (0x2CAE, "M", "ⲯ"), + (0x2CAF, "V"), + (0x2CB0, "M", "ⲱ"), + (0x2CB1, "V"), + (0x2CB2, "M", "ⲳ"), + (0x2CB3, "V"), + (0x2CB4, "M", "ⲵ"), + (0x2CB5, "V"), + (0x2CB6, "M", "ⲷ"), + (0x2CB7, "V"), + (0x2CB8, "M", "ⲹ"), + (0x2CB9, "V"), + (0x2CBA, "M", "ⲻ"), + (0x2CBB, "V"), + (0x2CBC, "M", "ⲽ"), + (0x2CBD, "V"), + (0x2CBE, "M", "ⲿ"), + (0x2CBF, "V"), + (0x2CC0, "M", "ⳁ"), + (0x2CC1, "V"), + (0x2CC2, "M", "ⳃ"), + (0x2CC3, "V"), + (0x2CC4, "M", "ⳅ"), + (0x2CC5, "V"), + (0x2CC6, "M", "ⳇ"), + ] + + +def _seg_26() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x2CC7, "V"), + (0x2CC8, "M", "ⳉ"), + (0x2CC9, "V"), + (0x2CCA, "M", "ⳋ"), + (0x2CCB, "V"), + (0x2CCC, "M", "ⳍ"), + (0x2CCD, "V"), + (0x2CCE, "M", "ⳏ"), + (0x2CCF, "V"), + (0x2CD0, "M", "ⳑ"), + (0x2CD1, "V"), + (0x2CD2, "M", "ⳓ"), + (0x2CD3, "V"), + (0x2CD4, "M", "ⳕ"), + (0x2CD5, "V"), + (0x2CD6, "M", "ⳗ"), + (0x2CD7, "V"), + (0x2CD8, "M", "ⳙ"), + (0x2CD9, "V"), + (0x2CDA, "M", "ⳛ"), + (0x2CDB, "V"), + (0x2CDC, "M", "ⳝ"), + (0x2CDD, "V"), + (0x2CDE, "M", "ⳟ"), + (0x2CDF, "V"), + (0x2CE0, "M", "ⳡ"), + (0x2CE1, "V"), + (0x2CE2, "M", "ⳣ"), + (0x2CE3, "V"), + (0x2CEB, "M", "ⳬ"), + (0x2CEC, "V"), + (0x2CED, "M", "ⳮ"), + (0x2CEE, "V"), + (0x2CF2, "M", "ⳳ"), + (0x2CF3, "V"), + (0x2CF4, "X"), + (0x2CF9, "V"), + (0x2D26, "X"), + (0x2D27, "V"), + (0x2D28, "X"), + (0x2D2D, "V"), + (0x2D2E, "X"), + (0x2D30, "V"), + (0x2D68, "X"), + (0x2D6F, "M", "ⵡ"), + (0x2D70, "V"), + (0x2D71, "X"), + (0x2D7F, "V"), + (0x2D97, "X"), + (0x2DA0, "V"), + (0x2DA7, "X"), + (0x2DA8, "V"), + (0x2DAF, "X"), + (0x2DB0, "V"), + (0x2DB7, "X"), + (0x2DB8, "V"), + (0x2DBF, "X"), + (0x2DC0, "V"), + (0x2DC7, "X"), + (0x2DC8, "V"), + (0x2DCF, "X"), + (0x2DD0, "V"), + (0x2DD7, "X"), + (0x2DD8, "V"), + (0x2DDF, "X"), + (0x2DE0, "V"), + (0x2E5E, "X"), + (0x2E80, "V"), + (0x2E9A, "X"), + (0x2E9B, "V"), + (0x2E9F, "M", "母"), + (0x2EA0, "V"), + (0x2EF3, "M", "龟"), + (0x2EF4, "X"), + (0x2F00, "M", "一"), + (0x2F01, "M", "丨"), + (0x2F02, "M", "丶"), + (0x2F03, "M", "丿"), + (0x2F04, "M", "乙"), + (0x2F05, "M", "亅"), + (0x2F06, "M", "二"), + (0x2F07, "M", "亠"), + (0x2F08, "M", "人"), + (0x2F09, "M", "儿"), + (0x2F0A, "M", "入"), + (0x2F0B, "M", "八"), + (0x2F0C, "M", "冂"), + (0x2F0D, "M", "冖"), + (0x2F0E, "M", "冫"), + (0x2F0F, "M", "几"), + (0x2F10, "M", "凵"), + (0x2F11, "M", "刀"), + (0x2F12, "M", "力"), + (0x2F13, "M", "勹"), + (0x2F14, "M", "匕"), + (0x2F15, "M", "匚"), + (0x2F16, "M", "匸"), + (0x2F17, "M", "十"), + (0x2F18, "M", "卜"), + (0x2F19, "M", "卩"), + ] + + +def _seg_27() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x2F1A, "M", "厂"), + (0x2F1B, "M", "厶"), + (0x2F1C, "M", "又"), + (0x2F1D, "M", "口"), + (0x2F1E, "M", "囗"), + (0x2F1F, "M", "土"), + (0x2F20, "M", "士"), + (0x2F21, "M", "夂"), + (0x2F22, "M", "夊"), + (0x2F23, "M", "夕"), + (0x2F24, "M", "大"), + (0x2F25, "M", "女"), + (0x2F26, "M", "子"), + (0x2F27, "M", "宀"), + (0x2F28, "M", "寸"), + (0x2F29, "M", "小"), + (0x2F2A, "M", "尢"), + (0x2F2B, "M", "尸"), + (0x2F2C, "M", "屮"), + (0x2F2D, "M", "山"), + (0x2F2E, "M", "巛"), + (0x2F2F, "M", "工"), + (0x2F30, "M", "己"), + (0x2F31, "M", "巾"), + (0x2F32, "M", "干"), + (0x2F33, "M", "幺"), + (0x2F34, "M", "广"), + (0x2F35, "M", "廴"), + (0x2F36, "M", "廾"), + (0x2F37, "M", "弋"), + (0x2F38, "M", "弓"), + (0x2F39, "M", "彐"), + (0x2F3A, "M", "彡"), + (0x2F3B, "M", "彳"), + (0x2F3C, "M", "心"), + (0x2F3D, "M", "戈"), + (0x2F3E, "M", "戶"), + (0x2F3F, "M", "手"), + (0x2F40, "M", "支"), + (0x2F41, "M", "攴"), + (0x2F42, "M", "文"), + (0x2F43, "M", "斗"), + (0x2F44, "M", "斤"), + (0x2F45, "M", "方"), + (0x2F46, "M", "无"), + (0x2F47, "M", "日"), + (0x2F48, "M", "曰"), + (0x2F49, "M", "月"), + (0x2F4A, "M", "木"), + (0x2F4B, "M", "欠"), + (0x2F4C, "M", "止"), + (0x2F4D, "M", "歹"), + (0x2F4E, "M", "殳"), + (0x2F4F, "M", "毋"), + (0x2F50, "M", "比"), + (0x2F51, "M", "毛"), + (0x2F52, "M", "氏"), + (0x2F53, "M", "气"), + (0x2F54, "M", "水"), + (0x2F55, "M", "火"), + (0x2F56, "M", "爪"), + (0x2F57, "M", "父"), + (0x2F58, "M", "爻"), + (0x2F59, "M", "爿"), + (0x2F5A, "M", "片"), + (0x2F5B, "M", "牙"), + (0x2F5C, "M", "牛"), + (0x2F5D, "M", "犬"), + (0x2F5E, "M", "玄"), + (0x2F5F, "M", "玉"), + (0x2F60, "M", "瓜"), + (0x2F61, "M", "瓦"), + (0x2F62, "M", "甘"), + (0x2F63, "M", "生"), + (0x2F64, "M", "用"), + (0x2F65, "M", "田"), + (0x2F66, "M", "疋"), + (0x2F67, "M", "疒"), + (0x2F68, "M", "癶"), + (0x2F69, "M", "白"), + (0x2F6A, "M", "皮"), + (0x2F6B, "M", "皿"), + (0x2F6C, "M", "目"), + (0x2F6D, "M", "矛"), + (0x2F6E, "M", "矢"), + (0x2F6F, "M", "石"), + (0x2F70, "M", "示"), + (0x2F71, "M", "禸"), + (0x2F72, "M", "禾"), + (0x2F73, "M", "穴"), + (0x2F74, "M", "立"), + (0x2F75, "M", "竹"), + (0x2F76, "M", "米"), + (0x2F77, "M", "糸"), + (0x2F78, "M", "缶"), + (0x2F79, "M", "网"), + (0x2F7A, "M", "羊"), + (0x2F7B, "M", "羽"), + (0x2F7C, "M", "老"), + (0x2F7D, "M", "而"), + ] + + +def _seg_28() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x2F7E, "M", "耒"), + (0x2F7F, "M", "耳"), + (0x2F80, "M", "聿"), + (0x2F81, "M", "肉"), + (0x2F82, "M", "臣"), + (0x2F83, "M", "自"), + (0x2F84, "M", "至"), + (0x2F85, "M", "臼"), + (0x2F86, "M", "舌"), + (0x2F87, "M", "舛"), + (0x2F88, "M", "舟"), + (0x2F89, "M", "艮"), + (0x2F8A, "M", "色"), + (0x2F8B, "M", "艸"), + (0x2F8C, "M", "虍"), + (0x2F8D, "M", "虫"), + (0x2F8E, "M", "血"), + (0x2F8F, "M", "行"), + (0x2F90, "M", "衣"), + (0x2F91, "M", "襾"), + (0x2F92, "M", "見"), + (0x2F93, "M", "角"), + (0x2F94, "M", "言"), + (0x2F95, "M", "谷"), + (0x2F96, "M", "豆"), + (0x2F97, "M", "豕"), + (0x2F98, "M", "豸"), + (0x2F99, "M", "貝"), + (0x2F9A, "M", "赤"), + (0x2F9B, "M", "走"), + (0x2F9C, "M", "足"), + (0x2F9D, "M", "身"), + (0x2F9E, "M", "車"), + (0x2F9F, "M", "辛"), + (0x2FA0, "M", "辰"), + (0x2FA1, "M", "辵"), + (0x2FA2, "M", "邑"), + (0x2FA3, "M", "酉"), + (0x2FA4, "M", "釆"), + (0x2FA5, "M", "里"), + (0x2FA6, "M", "金"), + (0x2FA7, "M", "長"), + (0x2FA8, "M", "門"), + (0x2FA9, "M", "阜"), + (0x2FAA, "M", "隶"), + (0x2FAB, "M", "隹"), + (0x2FAC, "M", "雨"), + (0x2FAD, "M", "靑"), + (0x2FAE, "M", "非"), + (0x2FAF, "M", "面"), + (0x2FB0, "M", "革"), + (0x2FB1, "M", "韋"), + (0x2FB2, "M", "韭"), + (0x2FB3, "M", "音"), + (0x2FB4, "M", "頁"), + (0x2FB5, "M", "風"), + (0x2FB6, "M", "飛"), + (0x2FB7, "M", "食"), + (0x2FB8, "M", "首"), + (0x2FB9, "M", "香"), + (0x2FBA, "M", "馬"), + (0x2FBB, "M", "骨"), + (0x2FBC, "M", "高"), + (0x2FBD, "M", "髟"), + (0x2FBE, "M", "鬥"), + (0x2FBF, "M", "鬯"), + (0x2FC0, "M", "鬲"), + (0x2FC1, "M", "鬼"), + (0x2FC2, "M", "魚"), + (0x2FC3, "M", "鳥"), + (0x2FC4, "M", "鹵"), + (0x2FC5, "M", "鹿"), + (0x2FC6, "M", "麥"), + (0x2FC7, "M", "麻"), + (0x2FC8, "M", "黃"), + (0x2FC9, "M", "黍"), + (0x2FCA, "M", "黑"), + (0x2FCB, "M", "黹"), + (0x2FCC, "M", "黽"), + (0x2FCD, "M", "鼎"), + (0x2FCE, "M", "鼓"), + (0x2FCF, "M", "鼠"), + (0x2FD0, "M", "鼻"), + (0x2FD1, "M", "齊"), + (0x2FD2, "M", "齒"), + (0x2FD3, "M", "龍"), + (0x2FD4, "M", "龜"), + (0x2FD5, "M", "龠"), + (0x2FD6, "X"), + (0x3000, "3", " "), + (0x3001, "V"), + (0x3002, "M", "."), + (0x3003, "V"), + (0x3036, "M", "〒"), + (0x3037, "V"), + (0x3038, "M", "十"), + (0x3039, "M", "卄"), + (0x303A, "M", "卅"), + (0x303B, "V"), + (0x3040, "X"), + ] + + +def _seg_29() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x3041, "V"), + (0x3097, "X"), + (0x3099, "V"), + (0x309B, "3", " ゙"), + (0x309C, "3", " ゚"), + (0x309D, "V"), + (0x309F, "M", "より"), + (0x30A0, "V"), + (0x30FF, "M", "コト"), + (0x3100, "X"), + (0x3105, "V"), + (0x3130, "X"), + (0x3131, "M", "ᄀ"), + (0x3132, "M", "ᄁ"), + (0x3133, "M", "ᆪ"), + (0x3134, "M", "ᄂ"), + (0x3135, "M", "ᆬ"), + (0x3136, "M", "ᆭ"), + (0x3137, "M", "ᄃ"), + (0x3138, "M", "ᄄ"), + (0x3139, "M", "ᄅ"), + (0x313A, "M", "ᆰ"), + (0x313B, "M", "ᆱ"), + (0x313C, "M", "ᆲ"), + (0x313D, "M", "ᆳ"), + (0x313E, "M", "ᆴ"), + (0x313F, "M", "ᆵ"), + (0x3140, "M", "ᄚ"), + (0x3141, "M", "ᄆ"), + (0x3142, "M", "ᄇ"), + (0x3143, "M", "ᄈ"), + (0x3144, "M", "ᄡ"), + (0x3145, "M", "ᄉ"), + (0x3146, "M", "ᄊ"), + (0x3147, "M", "ᄋ"), + (0x3148, "M", "ᄌ"), + (0x3149, "M", "ᄍ"), + (0x314A, "M", "ᄎ"), + (0x314B, "M", "ᄏ"), + (0x314C, "M", "ᄐ"), + (0x314D, "M", "ᄑ"), + (0x314E, "M", "ᄒ"), + (0x314F, "M", "ᅡ"), + (0x3150, "M", "ᅢ"), + (0x3151, "M", "ᅣ"), + (0x3152, "M", "ᅤ"), + (0x3153, "M", "ᅥ"), + (0x3154, "M", "ᅦ"), + (0x3155, "M", "ᅧ"), + (0x3156, "M", "ᅨ"), + (0x3157, "M", "ᅩ"), + (0x3158, "M", "ᅪ"), + (0x3159, "M", "ᅫ"), + (0x315A, "M", "ᅬ"), + (0x315B, "M", "ᅭ"), + (0x315C, "M", "ᅮ"), + (0x315D, "M", "ᅯ"), + (0x315E, "M", "ᅰ"), + (0x315F, "M", "ᅱ"), + (0x3160, "M", "ᅲ"), + (0x3161, "M", "ᅳ"), + (0x3162, "M", "ᅴ"), + (0x3163, "M", "ᅵ"), + (0x3164, "X"), + (0x3165, "M", "ᄔ"), + (0x3166, "M", "ᄕ"), + (0x3167, "M", "ᇇ"), + (0x3168, "M", "ᇈ"), + (0x3169, "M", "ᇌ"), + (0x316A, "M", "ᇎ"), + (0x316B, "M", "ᇓ"), + (0x316C, "M", "ᇗ"), + (0x316D, "M", "ᇙ"), + (0x316E, "M", "ᄜ"), + (0x316F, "M", "ᇝ"), + (0x3170, "M", "ᇟ"), + (0x3171, "M", "ᄝ"), + (0x3172, "M", "ᄞ"), + (0x3173, "M", "ᄠ"), + (0x3174, "M", "ᄢ"), + (0x3175, "M", "ᄣ"), + (0x3176, "M", "ᄧ"), + (0x3177, "M", "ᄩ"), + (0x3178, "M", "ᄫ"), + (0x3179, "M", "ᄬ"), + (0x317A, "M", "ᄭ"), + (0x317B, "M", "ᄮ"), + (0x317C, "M", "ᄯ"), + (0x317D, "M", "ᄲ"), + (0x317E, "M", "ᄶ"), + (0x317F, "M", "ᅀ"), + (0x3180, "M", "ᅇ"), + (0x3181, "M", "ᅌ"), + (0x3182, "M", "ᇱ"), + (0x3183, "M", "ᇲ"), + (0x3184, "M", "ᅗ"), + (0x3185, "M", "ᅘ"), + (0x3186, "M", "ᅙ"), + (0x3187, "M", "ᆄ"), + (0x3188, "M", "ᆅ"), + ] + + +def _seg_30() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x3189, "M", "ᆈ"), + (0x318A, "M", "ᆑ"), + (0x318B, "M", "ᆒ"), + (0x318C, "M", "ᆔ"), + (0x318D, "M", "ᆞ"), + (0x318E, "M", "ᆡ"), + (0x318F, "X"), + (0x3190, "V"), + (0x3192, "M", "一"), + (0x3193, "M", "二"), + (0x3194, "M", "三"), + (0x3195, "M", "四"), + (0x3196, "M", "上"), + (0x3197, "M", "中"), + (0x3198, "M", "下"), + (0x3199, "M", "甲"), + (0x319A, "M", "乙"), + (0x319B, "M", "丙"), + (0x319C, "M", "丁"), + (0x319D, "M", "天"), + (0x319E, "M", "地"), + (0x319F, "M", "人"), + (0x31A0, "V"), + (0x31E4, "X"), + (0x31F0, "V"), + (0x3200, "3", "(ᄀ)"), + (0x3201, "3", "(ᄂ)"), + (0x3202, "3", "(ᄃ)"), + (0x3203, "3", "(ᄅ)"), + (0x3204, "3", "(ᄆ)"), + (0x3205, "3", "(ᄇ)"), + (0x3206, "3", "(ᄉ)"), + (0x3207, "3", "(ᄋ)"), + (0x3208, "3", "(ᄌ)"), + (0x3209, "3", "(ᄎ)"), + (0x320A, "3", "(ᄏ)"), + (0x320B, "3", "(ᄐ)"), + (0x320C, "3", "(ᄑ)"), + (0x320D, "3", "(ᄒ)"), + (0x320E, "3", "(가)"), + (0x320F, "3", "(나)"), + (0x3210, "3", "(다)"), + (0x3211, "3", "(라)"), + (0x3212, "3", "(마)"), + (0x3213, "3", "(바)"), + (0x3214, "3", "(사)"), + (0x3215, "3", "(아)"), + (0x3216, "3", "(자)"), + (0x3217, "3", "(차)"), + (0x3218, "3", "(카)"), + (0x3219, "3", "(타)"), + (0x321A, "3", "(파)"), + (0x321B, "3", "(하)"), + (0x321C, "3", "(주)"), + (0x321D, "3", "(오전)"), + (0x321E, "3", "(오후)"), + (0x321F, "X"), + (0x3220, "3", "(一)"), + (0x3221, "3", "(二)"), + (0x3222, "3", "(三)"), + (0x3223, "3", "(四)"), + (0x3224, "3", "(五)"), + (0x3225, "3", "(六)"), + (0x3226, "3", "(七)"), + (0x3227, "3", "(八)"), + (0x3228, "3", "(九)"), + (0x3229, "3", "(十)"), + (0x322A, "3", "(月)"), + (0x322B, "3", "(火)"), + (0x322C, "3", "(水)"), + (0x322D, "3", "(木)"), + (0x322E, "3", "(金)"), + (0x322F, "3", "(土)"), + (0x3230, "3", "(日)"), + (0x3231, "3", "(株)"), + (0x3232, "3", "(有)"), + (0x3233, "3", "(社)"), + (0x3234, "3", "(名)"), + (0x3235, "3", "(特)"), + (0x3236, "3", "(財)"), + (0x3237, "3", "(祝)"), + (0x3238, "3", "(労)"), + (0x3239, "3", "(代)"), + (0x323A, "3", "(呼)"), + (0x323B, "3", "(学)"), + (0x323C, "3", "(監)"), + (0x323D, "3", "(企)"), + (0x323E, "3", "(資)"), + (0x323F, "3", "(協)"), + (0x3240, "3", "(祭)"), + (0x3241, "3", "(休)"), + (0x3242, "3", "(自)"), + (0x3243, "3", "(至)"), + (0x3244, "M", "問"), + (0x3245, "M", "幼"), + (0x3246, "M", "文"), + (0x3247, "M", "箏"), + (0x3248, "V"), + (0x3250, "M", "pte"), + (0x3251, "M", "21"), + ] + + +def _seg_31() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x3252, "M", "22"), + (0x3253, "M", "23"), + (0x3254, "M", "24"), + (0x3255, "M", "25"), + (0x3256, "M", "26"), + (0x3257, "M", "27"), + (0x3258, "M", "28"), + (0x3259, "M", "29"), + (0x325A, "M", "30"), + (0x325B, "M", "31"), + (0x325C, "M", "32"), + (0x325D, "M", "33"), + (0x325E, "M", "34"), + (0x325F, "M", "35"), + (0x3260, "M", "ᄀ"), + (0x3261, "M", "ᄂ"), + (0x3262, "M", "ᄃ"), + (0x3263, "M", "ᄅ"), + (0x3264, "M", "ᄆ"), + (0x3265, "M", "ᄇ"), + (0x3266, "M", "ᄉ"), + (0x3267, "M", "ᄋ"), + (0x3268, "M", "ᄌ"), + (0x3269, "M", "ᄎ"), + (0x326A, "M", "ᄏ"), + (0x326B, "M", "ᄐ"), + (0x326C, "M", "ᄑ"), + (0x326D, "M", "ᄒ"), + (0x326E, "M", "가"), + (0x326F, "M", "나"), + (0x3270, "M", "다"), + (0x3271, "M", "라"), + (0x3272, "M", "마"), + (0x3273, "M", "바"), + (0x3274, "M", "사"), + (0x3275, "M", "아"), + (0x3276, "M", "자"), + (0x3277, "M", "차"), + (0x3278, "M", "카"), + (0x3279, "M", "타"), + (0x327A, "M", "파"), + (0x327B, "M", "하"), + (0x327C, "M", "참고"), + (0x327D, "M", "주의"), + (0x327E, "M", "우"), + (0x327F, "V"), + (0x3280, "M", "一"), + (0x3281, "M", "二"), + (0x3282, "M", "三"), + (0x3283, "M", "四"), + (0x3284, "M", "五"), + (0x3285, "M", "六"), + (0x3286, "M", "七"), + (0x3287, "M", "八"), + (0x3288, "M", "九"), + (0x3289, "M", "十"), + (0x328A, "M", "月"), + (0x328B, "M", "火"), + (0x328C, "M", "水"), + (0x328D, "M", "木"), + (0x328E, "M", "金"), + (0x328F, "M", "土"), + (0x3290, "M", "日"), + (0x3291, "M", "株"), + (0x3292, "M", "有"), + (0x3293, "M", "社"), + (0x3294, "M", "名"), + (0x3295, "M", "特"), + (0x3296, "M", "財"), + (0x3297, "M", "祝"), + (0x3298, "M", "労"), + (0x3299, "M", "秘"), + (0x329A, "M", "男"), + (0x329B, "M", "女"), + (0x329C, "M", "適"), + (0x329D, "M", "優"), + (0x329E, "M", "印"), + (0x329F, "M", "注"), + (0x32A0, "M", "項"), + (0x32A1, "M", "休"), + (0x32A2, "M", "写"), + (0x32A3, "M", "正"), + (0x32A4, "M", "上"), + (0x32A5, "M", "中"), + (0x32A6, "M", "下"), + (0x32A7, "M", "左"), + (0x32A8, "M", "右"), + (0x32A9, "M", "医"), + (0x32AA, "M", "宗"), + (0x32AB, "M", "学"), + (0x32AC, "M", "監"), + (0x32AD, "M", "企"), + (0x32AE, "M", "資"), + (0x32AF, "M", "協"), + (0x32B0, "M", "夜"), + (0x32B1, "M", "36"), + (0x32B2, "M", "37"), + (0x32B3, "M", "38"), + (0x32B4, "M", "39"), + (0x32B5, "M", "40"), + ] + + +def _seg_32() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x32B6, "M", "41"), + (0x32B7, "M", "42"), + (0x32B8, "M", "43"), + (0x32B9, "M", "44"), + (0x32BA, "M", "45"), + (0x32BB, "M", "46"), + (0x32BC, "M", "47"), + (0x32BD, "M", "48"), + (0x32BE, "M", "49"), + (0x32BF, "M", "50"), + (0x32C0, "M", "1月"), + (0x32C1, "M", "2月"), + (0x32C2, "M", "3月"), + (0x32C3, "M", "4月"), + (0x32C4, "M", "5月"), + (0x32C5, "M", "6月"), + (0x32C6, "M", "7月"), + (0x32C7, "M", "8月"), + (0x32C8, "M", "9月"), + (0x32C9, "M", "10月"), + (0x32CA, "M", "11月"), + (0x32CB, "M", "12月"), + (0x32CC, "M", "hg"), + (0x32CD, "M", "erg"), + (0x32CE, "M", "ev"), + (0x32CF, "M", "ltd"), + (0x32D0, "M", "ア"), + (0x32D1, "M", "イ"), + (0x32D2, "M", "ウ"), + (0x32D3, "M", "エ"), + (0x32D4, "M", "オ"), + (0x32D5, "M", "カ"), + (0x32D6, "M", "キ"), + (0x32D7, "M", "ク"), + (0x32D8, "M", "ケ"), + (0x32D9, "M", "コ"), + (0x32DA, "M", "サ"), + (0x32DB, "M", "シ"), + (0x32DC, "M", "ス"), + (0x32DD, "M", "セ"), + (0x32DE, "M", "ソ"), + (0x32DF, "M", "タ"), + (0x32E0, "M", "チ"), + (0x32E1, "M", "ツ"), + (0x32E2, "M", "テ"), + (0x32E3, "M", "ト"), + (0x32E4, "M", "ナ"), + (0x32E5, "M", "ニ"), + (0x32E6, "M", "ヌ"), + (0x32E7, "M", "ネ"), + (0x32E8, "M", "ノ"), + (0x32E9, "M", "ハ"), + (0x32EA, "M", "ヒ"), + (0x32EB, "M", "フ"), + (0x32EC, "M", "ヘ"), + (0x32ED, "M", "ホ"), + (0x32EE, "M", "マ"), + (0x32EF, "M", "ミ"), + (0x32F0, "M", "ム"), + (0x32F1, "M", "メ"), + (0x32F2, "M", "モ"), + (0x32F3, "M", "ヤ"), + (0x32F4, "M", "ユ"), + (0x32F5, "M", "ヨ"), + (0x32F6, "M", "ラ"), + (0x32F7, "M", "リ"), + (0x32F8, "M", "ル"), + (0x32F9, "M", "レ"), + (0x32FA, "M", "ロ"), + (0x32FB, "M", "ワ"), + (0x32FC, "M", "ヰ"), + (0x32FD, "M", "ヱ"), + (0x32FE, "M", "ヲ"), + (0x32FF, "M", "令和"), + (0x3300, "M", "アパート"), + (0x3301, "M", "アルファ"), + (0x3302, "M", "アンペア"), + (0x3303, "M", "アール"), + (0x3304, "M", "イニング"), + (0x3305, "M", "インチ"), + (0x3306, "M", "ウォン"), + (0x3307, "M", "エスクード"), + (0x3308, "M", "エーカー"), + (0x3309, "M", "オンス"), + (0x330A, "M", "オーム"), + (0x330B, "M", "カイリ"), + (0x330C, "M", "カラット"), + (0x330D, "M", "カロリー"), + (0x330E, "M", "ガロン"), + (0x330F, "M", "ガンマ"), + (0x3310, "M", "ギガ"), + (0x3311, "M", "ギニー"), + (0x3312, "M", "キュリー"), + (0x3313, "M", "ギルダー"), + (0x3314, "M", "キロ"), + (0x3315, "M", "キログラム"), + (0x3316, "M", "キロメートル"), + (0x3317, "M", "キロワット"), + (0x3318, "M", "グラム"), + (0x3319, "M", "グラムトン"), + ] + + +def _seg_33() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x331A, "M", "クルゼイロ"), + (0x331B, "M", "クローネ"), + (0x331C, "M", "ケース"), + (0x331D, "M", "コルナ"), + (0x331E, "M", "コーポ"), + (0x331F, "M", "サイクル"), + (0x3320, "M", "サンチーム"), + (0x3321, "M", "シリング"), + (0x3322, "M", "センチ"), + (0x3323, "M", "セント"), + (0x3324, "M", "ダース"), + (0x3325, "M", "デシ"), + (0x3326, "M", "ドル"), + (0x3327, "M", "トン"), + (0x3328, "M", "ナノ"), + (0x3329, "M", "ノット"), + (0x332A, "M", "ハイツ"), + (0x332B, "M", "パーセント"), + (0x332C, "M", "パーツ"), + (0x332D, "M", "バーレル"), + (0x332E, "M", "ピアストル"), + (0x332F, "M", "ピクル"), + (0x3330, "M", "ピコ"), + (0x3331, "M", "ビル"), + (0x3332, "M", "ファラッド"), + (0x3333, "M", "フィート"), + (0x3334, "M", "ブッシェル"), + (0x3335, "M", "フラン"), + (0x3336, "M", "ヘクタール"), + (0x3337, "M", "ペソ"), + (0x3338, "M", "ペニヒ"), + (0x3339, "M", "ヘルツ"), + (0x333A, "M", "ペンス"), + (0x333B, "M", "ページ"), + (0x333C, "M", "ベータ"), + (0x333D, "M", "ポイント"), + (0x333E, "M", "ボルト"), + (0x333F, "M", "ホン"), + (0x3340, "M", "ポンド"), + (0x3341, "M", "ホール"), + (0x3342, "M", "ホーン"), + (0x3343, "M", "マイクロ"), + (0x3344, "M", "マイル"), + (0x3345, "M", "マッハ"), + (0x3346, "M", "マルク"), + (0x3347, "M", "マンション"), + (0x3348, "M", "ミクロン"), + (0x3349, "M", "ミリ"), + (0x334A, "M", "ミリバール"), + (0x334B, "M", "メガ"), + (0x334C, "M", "メガトン"), + (0x334D, "M", "メートル"), + (0x334E, "M", "ヤード"), + (0x334F, "M", "ヤール"), + (0x3350, "M", "ユアン"), + (0x3351, "M", "リットル"), + (0x3352, "M", "リラ"), + (0x3353, "M", "ルピー"), + (0x3354, "M", "ルーブル"), + (0x3355, "M", "レム"), + (0x3356, "M", "レントゲン"), + (0x3357, "M", "ワット"), + (0x3358, "M", "0点"), + (0x3359, "M", "1点"), + (0x335A, "M", "2点"), + (0x335B, "M", "3点"), + (0x335C, "M", "4点"), + (0x335D, "M", "5点"), + (0x335E, "M", "6点"), + (0x335F, "M", "7点"), + (0x3360, "M", "8点"), + (0x3361, "M", "9点"), + (0x3362, "M", "10点"), + (0x3363, "M", "11点"), + (0x3364, "M", "12点"), + (0x3365, "M", "13点"), + (0x3366, "M", "14点"), + (0x3367, "M", "15点"), + (0x3368, "M", "16点"), + (0x3369, "M", "17点"), + (0x336A, "M", "18点"), + (0x336B, "M", "19点"), + (0x336C, "M", "20点"), + (0x336D, "M", "21点"), + (0x336E, "M", "22点"), + (0x336F, "M", "23点"), + (0x3370, "M", "24点"), + (0x3371, "M", "hpa"), + (0x3372, "M", "da"), + (0x3373, "M", "au"), + (0x3374, "M", "bar"), + (0x3375, "M", "ov"), + (0x3376, "M", "pc"), + (0x3377, "M", "dm"), + (0x3378, "M", "dm2"), + (0x3379, "M", "dm3"), + (0x337A, "M", "iu"), + (0x337B, "M", "平成"), + (0x337C, "M", "昭和"), + (0x337D, "M", "大正"), + ] + + +def _seg_34() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x337E, "M", "明治"), + (0x337F, "M", "株式会社"), + (0x3380, "M", "pa"), + (0x3381, "M", "na"), + (0x3382, "M", "μa"), + (0x3383, "M", "ma"), + (0x3384, "M", "ka"), + (0x3385, "M", "kb"), + (0x3386, "M", "mb"), + (0x3387, "M", "gb"), + (0x3388, "M", "cal"), + (0x3389, "M", "kcal"), + (0x338A, "M", "pf"), + (0x338B, "M", "nf"), + (0x338C, "M", "μf"), + (0x338D, "M", "μg"), + (0x338E, "M", "mg"), + (0x338F, "M", "kg"), + (0x3390, "M", "hz"), + (0x3391, "M", "khz"), + (0x3392, "M", "mhz"), + (0x3393, "M", "ghz"), + (0x3394, "M", "thz"), + (0x3395, "M", "μl"), + (0x3396, "M", "ml"), + (0x3397, "M", "dl"), + (0x3398, "M", "kl"), + (0x3399, "M", "fm"), + (0x339A, "M", "nm"), + (0x339B, "M", "μm"), + (0x339C, "M", "mm"), + (0x339D, "M", "cm"), + (0x339E, "M", "km"), + (0x339F, "M", "mm2"), + (0x33A0, "M", "cm2"), + (0x33A1, "M", "m2"), + (0x33A2, "M", "km2"), + (0x33A3, "M", "mm3"), + (0x33A4, "M", "cm3"), + (0x33A5, "M", "m3"), + (0x33A6, "M", "km3"), + (0x33A7, "M", "m∕s"), + (0x33A8, "M", "m∕s2"), + (0x33A9, "M", "pa"), + (0x33AA, "M", "kpa"), + (0x33AB, "M", "mpa"), + (0x33AC, "M", "gpa"), + (0x33AD, "M", "rad"), + (0x33AE, "M", "rad∕s"), + (0x33AF, "M", "rad∕s2"), + (0x33B0, "M", "ps"), + (0x33B1, "M", "ns"), + (0x33B2, "M", "μs"), + (0x33B3, "M", "ms"), + (0x33B4, "M", "pv"), + (0x33B5, "M", "nv"), + (0x33B6, "M", "μv"), + (0x33B7, "M", "mv"), + (0x33B8, "M", "kv"), + (0x33B9, "M", "mv"), + (0x33BA, "M", "pw"), + (0x33BB, "M", "nw"), + (0x33BC, "M", "μw"), + (0x33BD, "M", "mw"), + (0x33BE, "M", "kw"), + (0x33BF, "M", "mw"), + (0x33C0, "M", "kω"), + (0x33C1, "M", "mω"), + (0x33C2, "X"), + (0x33C3, "M", "bq"), + (0x33C4, "M", "cc"), + (0x33C5, "M", "cd"), + (0x33C6, "M", "c∕kg"), + (0x33C7, "X"), + (0x33C8, "M", "db"), + (0x33C9, "M", "gy"), + (0x33CA, "M", "ha"), + (0x33CB, "M", "hp"), + (0x33CC, "M", "in"), + (0x33CD, "M", "kk"), + (0x33CE, "M", "km"), + (0x33CF, "M", "kt"), + (0x33D0, "M", "lm"), + (0x33D1, "M", "ln"), + (0x33D2, "M", "log"), + (0x33D3, "M", "lx"), + (0x33D4, "M", "mb"), + (0x33D5, "M", "mil"), + (0x33D6, "M", "mol"), + (0x33D7, "M", "ph"), + (0x33D8, "X"), + (0x33D9, "M", "ppm"), + (0x33DA, "M", "pr"), + (0x33DB, "M", "sr"), + (0x33DC, "M", "sv"), + (0x33DD, "M", "wb"), + (0x33DE, "M", "v∕m"), + (0x33DF, "M", "a∕m"), + (0x33E0, "M", "1日"), + (0x33E1, "M", "2日"), + ] + + +def _seg_35() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x33E2, "M", "3日"), + (0x33E3, "M", "4日"), + (0x33E4, "M", "5日"), + (0x33E5, "M", "6日"), + (0x33E6, "M", "7日"), + (0x33E7, "M", "8日"), + (0x33E8, "M", "9日"), + (0x33E9, "M", "10日"), + (0x33EA, "M", "11日"), + (0x33EB, "M", "12日"), + (0x33EC, "M", "13日"), + (0x33ED, "M", "14日"), + (0x33EE, "M", "15日"), + (0x33EF, "M", "16日"), + (0x33F0, "M", "17日"), + (0x33F1, "M", "18日"), + (0x33F2, "M", "19日"), + (0x33F3, "M", "20日"), + (0x33F4, "M", "21日"), + (0x33F5, "M", "22日"), + (0x33F6, "M", "23日"), + (0x33F7, "M", "24日"), + (0x33F8, "M", "25日"), + (0x33F9, "M", "26日"), + (0x33FA, "M", "27日"), + (0x33FB, "M", "28日"), + (0x33FC, "M", "29日"), + (0x33FD, "M", "30日"), + (0x33FE, "M", "31日"), + (0x33FF, "M", "gal"), + (0x3400, "V"), + (0xA48D, "X"), + (0xA490, "V"), + (0xA4C7, "X"), + (0xA4D0, "V"), + (0xA62C, "X"), + (0xA640, "M", "ꙁ"), + (0xA641, "V"), + (0xA642, "M", "ꙃ"), + (0xA643, "V"), + (0xA644, "M", "ꙅ"), + (0xA645, "V"), + (0xA646, "M", "ꙇ"), + (0xA647, "V"), + (0xA648, "M", "ꙉ"), + (0xA649, "V"), + (0xA64A, "M", "ꙋ"), + (0xA64B, "V"), + (0xA64C, "M", "ꙍ"), + (0xA64D, "V"), + (0xA64E, "M", "ꙏ"), + (0xA64F, "V"), + (0xA650, "M", "ꙑ"), + (0xA651, "V"), + (0xA652, "M", "ꙓ"), + (0xA653, "V"), + (0xA654, "M", "ꙕ"), + (0xA655, "V"), + (0xA656, "M", "ꙗ"), + (0xA657, "V"), + (0xA658, "M", "ꙙ"), + (0xA659, "V"), + (0xA65A, "M", "ꙛ"), + (0xA65B, "V"), + (0xA65C, "M", "ꙝ"), + (0xA65D, "V"), + (0xA65E, "M", "ꙟ"), + (0xA65F, "V"), + (0xA660, "M", "ꙡ"), + (0xA661, "V"), + (0xA662, "M", "ꙣ"), + (0xA663, "V"), + (0xA664, "M", "ꙥ"), + (0xA665, "V"), + (0xA666, "M", "ꙧ"), + (0xA667, "V"), + (0xA668, "M", "ꙩ"), + (0xA669, "V"), + (0xA66A, "M", "ꙫ"), + (0xA66B, "V"), + (0xA66C, "M", "ꙭ"), + (0xA66D, "V"), + (0xA680, "M", "ꚁ"), + (0xA681, "V"), + (0xA682, "M", "ꚃ"), + (0xA683, "V"), + (0xA684, "M", "ꚅ"), + (0xA685, "V"), + (0xA686, "M", "ꚇ"), + (0xA687, "V"), + (0xA688, "M", "ꚉ"), + (0xA689, "V"), + (0xA68A, "M", "ꚋ"), + (0xA68B, "V"), + (0xA68C, "M", "ꚍ"), + (0xA68D, "V"), + (0xA68E, "M", "ꚏ"), + (0xA68F, "V"), + (0xA690, "M", "ꚑ"), + (0xA691, "V"), + ] + + +def _seg_36() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xA692, "M", "ꚓ"), + (0xA693, "V"), + (0xA694, "M", "ꚕ"), + (0xA695, "V"), + (0xA696, "M", "ꚗ"), + (0xA697, "V"), + (0xA698, "M", "ꚙ"), + (0xA699, "V"), + (0xA69A, "M", "ꚛ"), + (0xA69B, "V"), + (0xA69C, "M", "ъ"), + (0xA69D, "M", "ь"), + (0xA69E, "V"), + (0xA6F8, "X"), + (0xA700, "V"), + (0xA722, "M", "ꜣ"), + (0xA723, "V"), + (0xA724, "M", "ꜥ"), + (0xA725, "V"), + (0xA726, "M", "ꜧ"), + (0xA727, "V"), + (0xA728, "M", "ꜩ"), + (0xA729, "V"), + (0xA72A, "M", "ꜫ"), + (0xA72B, "V"), + (0xA72C, "M", "ꜭ"), + (0xA72D, "V"), + (0xA72E, "M", "ꜯ"), + (0xA72F, "V"), + (0xA732, "M", "ꜳ"), + (0xA733, "V"), + (0xA734, "M", "ꜵ"), + (0xA735, "V"), + (0xA736, "M", "ꜷ"), + (0xA737, "V"), + (0xA738, "M", "ꜹ"), + (0xA739, "V"), + (0xA73A, "M", "ꜻ"), + (0xA73B, "V"), + (0xA73C, "M", "ꜽ"), + (0xA73D, "V"), + (0xA73E, "M", "ꜿ"), + (0xA73F, "V"), + (0xA740, "M", "ꝁ"), + (0xA741, "V"), + (0xA742, "M", "ꝃ"), + (0xA743, "V"), + (0xA744, "M", "ꝅ"), + (0xA745, "V"), + (0xA746, "M", "ꝇ"), + (0xA747, "V"), + (0xA748, "M", "ꝉ"), + (0xA749, "V"), + (0xA74A, "M", "ꝋ"), + (0xA74B, "V"), + (0xA74C, "M", "ꝍ"), + (0xA74D, "V"), + (0xA74E, "M", "ꝏ"), + (0xA74F, "V"), + (0xA750, "M", "ꝑ"), + (0xA751, "V"), + (0xA752, "M", "ꝓ"), + (0xA753, "V"), + (0xA754, "M", "ꝕ"), + (0xA755, "V"), + (0xA756, "M", "ꝗ"), + (0xA757, "V"), + (0xA758, "M", "ꝙ"), + (0xA759, "V"), + (0xA75A, "M", "ꝛ"), + (0xA75B, "V"), + (0xA75C, "M", "ꝝ"), + (0xA75D, "V"), + (0xA75E, "M", "ꝟ"), + (0xA75F, "V"), + (0xA760, "M", "ꝡ"), + (0xA761, "V"), + (0xA762, "M", "ꝣ"), + (0xA763, "V"), + (0xA764, "M", "ꝥ"), + (0xA765, "V"), + (0xA766, "M", "ꝧ"), + (0xA767, "V"), + (0xA768, "M", "ꝩ"), + (0xA769, "V"), + (0xA76A, "M", "ꝫ"), + (0xA76B, "V"), + (0xA76C, "M", "ꝭ"), + (0xA76D, "V"), + (0xA76E, "M", "ꝯ"), + (0xA76F, "V"), + (0xA770, "M", "ꝯ"), + (0xA771, "V"), + (0xA779, "M", "ꝺ"), + (0xA77A, "V"), + (0xA77B, "M", "ꝼ"), + (0xA77C, "V"), + (0xA77D, "M", "ᵹ"), + (0xA77E, "M", "ꝿ"), + (0xA77F, "V"), + ] + + +def _seg_37() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xA780, "M", "ꞁ"), + (0xA781, "V"), + (0xA782, "M", "ꞃ"), + (0xA783, "V"), + (0xA784, "M", "ꞅ"), + (0xA785, "V"), + (0xA786, "M", "ꞇ"), + (0xA787, "V"), + (0xA78B, "M", "ꞌ"), + (0xA78C, "V"), + (0xA78D, "M", "ɥ"), + (0xA78E, "V"), + (0xA790, "M", "ꞑ"), + (0xA791, "V"), + (0xA792, "M", "ꞓ"), + (0xA793, "V"), + (0xA796, "M", "ꞗ"), + (0xA797, "V"), + (0xA798, "M", "ꞙ"), + (0xA799, "V"), + (0xA79A, "M", "ꞛ"), + (0xA79B, "V"), + (0xA79C, "M", "ꞝ"), + (0xA79D, "V"), + (0xA79E, "M", "ꞟ"), + (0xA79F, "V"), + (0xA7A0, "M", "ꞡ"), + (0xA7A1, "V"), + (0xA7A2, "M", "ꞣ"), + (0xA7A3, "V"), + (0xA7A4, "M", "ꞥ"), + (0xA7A5, "V"), + (0xA7A6, "M", "ꞧ"), + (0xA7A7, "V"), + (0xA7A8, "M", "ꞩ"), + (0xA7A9, "V"), + (0xA7AA, "M", "ɦ"), + (0xA7AB, "M", "ɜ"), + (0xA7AC, "M", "ɡ"), + (0xA7AD, "M", "ɬ"), + (0xA7AE, "M", "ɪ"), + (0xA7AF, "V"), + (0xA7B0, "M", "ʞ"), + (0xA7B1, "M", "ʇ"), + (0xA7B2, "M", "ʝ"), + (0xA7B3, "M", "ꭓ"), + (0xA7B4, "M", "ꞵ"), + (0xA7B5, "V"), + (0xA7B6, "M", "ꞷ"), + (0xA7B7, "V"), + (0xA7B8, "M", "ꞹ"), + (0xA7B9, "V"), + (0xA7BA, "M", "ꞻ"), + (0xA7BB, "V"), + (0xA7BC, "M", "ꞽ"), + (0xA7BD, "V"), + (0xA7BE, "M", "ꞿ"), + (0xA7BF, "V"), + (0xA7C0, "M", "ꟁ"), + (0xA7C1, "V"), + (0xA7C2, "M", "ꟃ"), + (0xA7C3, "V"), + (0xA7C4, "M", "ꞔ"), + (0xA7C5, "M", "ʂ"), + (0xA7C6, "M", "ᶎ"), + (0xA7C7, "M", "ꟈ"), + (0xA7C8, "V"), + (0xA7C9, "M", "ꟊ"), + (0xA7CA, "V"), + (0xA7CB, "X"), + (0xA7D0, "M", "ꟑ"), + (0xA7D1, "V"), + (0xA7D2, "X"), + (0xA7D3, "V"), + (0xA7D4, "X"), + (0xA7D5, "V"), + (0xA7D6, "M", "ꟗ"), + (0xA7D7, "V"), + (0xA7D8, "M", "ꟙ"), + (0xA7D9, "V"), + (0xA7DA, "X"), + (0xA7F2, "M", "c"), + (0xA7F3, "M", "f"), + (0xA7F4, "M", "q"), + (0xA7F5, "M", "ꟶ"), + (0xA7F6, "V"), + (0xA7F8, "M", "ħ"), + (0xA7F9, "M", "œ"), + (0xA7FA, "V"), + (0xA82D, "X"), + (0xA830, "V"), + (0xA83A, "X"), + (0xA840, "V"), + (0xA878, "X"), + (0xA880, "V"), + (0xA8C6, "X"), + (0xA8CE, "V"), + (0xA8DA, "X"), + (0xA8E0, "V"), + (0xA954, "X"), + ] + + +def _seg_38() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xA95F, "V"), + (0xA97D, "X"), + (0xA980, "V"), + (0xA9CE, "X"), + (0xA9CF, "V"), + (0xA9DA, "X"), + (0xA9DE, "V"), + (0xA9FF, "X"), + (0xAA00, "V"), + (0xAA37, "X"), + (0xAA40, "V"), + (0xAA4E, "X"), + (0xAA50, "V"), + (0xAA5A, "X"), + (0xAA5C, "V"), + (0xAAC3, "X"), + (0xAADB, "V"), + (0xAAF7, "X"), + (0xAB01, "V"), + (0xAB07, "X"), + (0xAB09, "V"), + (0xAB0F, "X"), + (0xAB11, "V"), + (0xAB17, "X"), + (0xAB20, "V"), + (0xAB27, "X"), + (0xAB28, "V"), + (0xAB2F, "X"), + (0xAB30, "V"), + (0xAB5C, "M", "ꜧ"), + (0xAB5D, "M", "ꬷ"), + (0xAB5E, "M", "ɫ"), + (0xAB5F, "M", "ꭒ"), + (0xAB60, "V"), + (0xAB69, "M", "ʍ"), + (0xAB6A, "V"), + (0xAB6C, "X"), + (0xAB70, "M", "Ꭰ"), + (0xAB71, "M", "Ꭱ"), + (0xAB72, "M", "Ꭲ"), + (0xAB73, "M", "Ꭳ"), + (0xAB74, "M", "Ꭴ"), + (0xAB75, "M", "Ꭵ"), + (0xAB76, "M", "Ꭶ"), + (0xAB77, "M", "Ꭷ"), + (0xAB78, "M", "Ꭸ"), + (0xAB79, "M", "Ꭹ"), + (0xAB7A, "M", "Ꭺ"), + (0xAB7B, "M", "Ꭻ"), + (0xAB7C, "M", "Ꭼ"), + (0xAB7D, "M", "Ꭽ"), + (0xAB7E, "M", "Ꭾ"), + (0xAB7F, "M", "Ꭿ"), + (0xAB80, "M", "Ꮀ"), + (0xAB81, "M", "Ꮁ"), + (0xAB82, "M", "Ꮂ"), + (0xAB83, "M", "Ꮃ"), + (0xAB84, "M", "Ꮄ"), + (0xAB85, "M", "Ꮅ"), + (0xAB86, "M", "Ꮆ"), + (0xAB87, "M", "Ꮇ"), + (0xAB88, "M", "Ꮈ"), + (0xAB89, "M", "Ꮉ"), + (0xAB8A, "M", "Ꮊ"), + (0xAB8B, "M", "Ꮋ"), + (0xAB8C, "M", "Ꮌ"), + (0xAB8D, "M", "Ꮍ"), + (0xAB8E, "M", "Ꮎ"), + (0xAB8F, "M", "Ꮏ"), + (0xAB90, "M", "Ꮐ"), + (0xAB91, "M", "Ꮑ"), + (0xAB92, "M", "Ꮒ"), + (0xAB93, "M", "Ꮓ"), + (0xAB94, "M", "Ꮔ"), + (0xAB95, "M", "Ꮕ"), + (0xAB96, "M", "Ꮖ"), + (0xAB97, "M", "Ꮗ"), + (0xAB98, "M", "Ꮘ"), + (0xAB99, "M", "Ꮙ"), + (0xAB9A, "M", "Ꮚ"), + (0xAB9B, "M", "Ꮛ"), + (0xAB9C, "M", "Ꮜ"), + (0xAB9D, "M", "Ꮝ"), + (0xAB9E, "M", "Ꮞ"), + (0xAB9F, "M", "Ꮟ"), + (0xABA0, "M", "Ꮠ"), + (0xABA1, "M", "Ꮡ"), + (0xABA2, "M", "Ꮢ"), + (0xABA3, "M", "Ꮣ"), + (0xABA4, "M", "Ꮤ"), + (0xABA5, "M", "Ꮥ"), + (0xABA6, "M", "Ꮦ"), + (0xABA7, "M", "Ꮧ"), + (0xABA8, "M", "Ꮨ"), + (0xABA9, "M", "Ꮩ"), + (0xABAA, "M", "Ꮪ"), + (0xABAB, "M", "Ꮫ"), + (0xABAC, "M", "Ꮬ"), + (0xABAD, "M", "Ꮭ"), + (0xABAE, "M", "Ꮮ"), + ] + + +def _seg_39() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xABAF, "M", "Ꮯ"), + (0xABB0, "M", "Ꮰ"), + (0xABB1, "M", "Ꮱ"), + (0xABB2, "M", "Ꮲ"), + (0xABB3, "M", "Ꮳ"), + (0xABB4, "M", "Ꮴ"), + (0xABB5, "M", "Ꮵ"), + (0xABB6, "M", "Ꮶ"), + (0xABB7, "M", "Ꮷ"), + (0xABB8, "M", "Ꮸ"), + (0xABB9, "M", "Ꮹ"), + (0xABBA, "M", "Ꮺ"), + (0xABBB, "M", "Ꮻ"), + (0xABBC, "M", "Ꮼ"), + (0xABBD, "M", "Ꮽ"), + (0xABBE, "M", "Ꮾ"), + (0xABBF, "M", "Ꮿ"), + (0xABC0, "V"), + (0xABEE, "X"), + (0xABF0, "V"), + (0xABFA, "X"), + (0xAC00, "V"), + (0xD7A4, "X"), + (0xD7B0, "V"), + (0xD7C7, "X"), + (0xD7CB, "V"), + (0xD7FC, "X"), + (0xF900, "M", "豈"), + (0xF901, "M", "更"), + (0xF902, "M", "車"), + (0xF903, "M", "賈"), + (0xF904, "M", "滑"), + (0xF905, "M", "串"), + (0xF906, "M", "句"), + (0xF907, "M", "龜"), + (0xF909, "M", "契"), + (0xF90A, "M", "金"), + (0xF90B, "M", "喇"), + (0xF90C, "M", "奈"), + (0xF90D, "M", "懶"), + (0xF90E, "M", "癩"), + (0xF90F, "M", "羅"), + (0xF910, "M", "蘿"), + (0xF911, "M", "螺"), + (0xF912, "M", "裸"), + (0xF913, "M", "邏"), + (0xF914, "M", "樂"), + (0xF915, "M", "洛"), + (0xF916, "M", "烙"), + (0xF917, "M", "珞"), + (0xF918, "M", "落"), + (0xF919, "M", "酪"), + (0xF91A, "M", "駱"), + (0xF91B, "M", "亂"), + (0xF91C, "M", "卵"), + (0xF91D, "M", "欄"), + (0xF91E, "M", "爛"), + (0xF91F, "M", "蘭"), + (0xF920, "M", "鸞"), + (0xF921, "M", "嵐"), + (0xF922, "M", "濫"), + (0xF923, "M", "藍"), + (0xF924, "M", "襤"), + (0xF925, "M", "拉"), + (0xF926, "M", "臘"), + (0xF927, "M", "蠟"), + (0xF928, "M", "廊"), + (0xF929, "M", "朗"), + (0xF92A, "M", "浪"), + (0xF92B, "M", "狼"), + (0xF92C, "M", "郎"), + (0xF92D, "M", "來"), + (0xF92E, "M", "冷"), + (0xF92F, "M", "勞"), + (0xF930, "M", "擄"), + (0xF931, "M", "櫓"), + (0xF932, "M", "爐"), + (0xF933, "M", "盧"), + (0xF934, "M", "老"), + (0xF935, "M", "蘆"), + (0xF936, "M", "虜"), + (0xF937, "M", "路"), + (0xF938, "M", "露"), + (0xF939, "M", "魯"), + (0xF93A, "M", "鷺"), + (0xF93B, "M", "碌"), + (0xF93C, "M", "祿"), + (0xF93D, "M", "綠"), + (0xF93E, "M", "菉"), + (0xF93F, "M", "錄"), + (0xF940, "M", "鹿"), + (0xF941, "M", "論"), + (0xF942, "M", "壟"), + (0xF943, "M", "弄"), + (0xF944, "M", "籠"), + (0xF945, "M", "聾"), + (0xF946, "M", "牢"), + (0xF947, "M", "磊"), + (0xF948, "M", "賂"), + (0xF949, "M", "雷"), + ] + + +def _seg_40() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xF94A, "M", "壘"), + (0xF94B, "M", "屢"), + (0xF94C, "M", "樓"), + (0xF94D, "M", "淚"), + (0xF94E, "M", "漏"), + (0xF94F, "M", "累"), + (0xF950, "M", "縷"), + (0xF951, "M", "陋"), + (0xF952, "M", "勒"), + (0xF953, "M", "肋"), + (0xF954, "M", "凜"), + (0xF955, "M", "凌"), + (0xF956, "M", "稜"), + (0xF957, "M", "綾"), + (0xF958, "M", "菱"), + (0xF959, "M", "陵"), + (0xF95A, "M", "讀"), + (0xF95B, "M", "拏"), + (0xF95C, "M", "樂"), + (0xF95D, "M", "諾"), + (0xF95E, "M", "丹"), + (0xF95F, "M", "寧"), + (0xF960, "M", "怒"), + (0xF961, "M", "率"), + (0xF962, "M", "異"), + (0xF963, "M", "北"), + (0xF964, "M", "磻"), + (0xF965, "M", "便"), + (0xF966, "M", "復"), + (0xF967, "M", "不"), + (0xF968, "M", "泌"), + (0xF969, "M", "數"), + (0xF96A, "M", "索"), + (0xF96B, "M", "參"), + (0xF96C, "M", "塞"), + (0xF96D, "M", "省"), + (0xF96E, "M", "葉"), + (0xF96F, "M", "說"), + (0xF970, "M", "殺"), + (0xF971, "M", "辰"), + (0xF972, "M", "沈"), + (0xF973, "M", "拾"), + (0xF974, "M", "若"), + (0xF975, "M", "掠"), + (0xF976, "M", "略"), + (0xF977, "M", "亮"), + (0xF978, "M", "兩"), + (0xF979, "M", "凉"), + (0xF97A, "M", "梁"), + (0xF97B, "M", "糧"), + (0xF97C, "M", "良"), + (0xF97D, "M", "諒"), + (0xF97E, "M", "量"), + (0xF97F, "M", "勵"), + (0xF980, "M", "呂"), + (0xF981, "M", "女"), + (0xF982, "M", "廬"), + (0xF983, "M", "旅"), + (0xF984, "M", "濾"), + (0xF985, "M", "礪"), + (0xF986, "M", "閭"), + (0xF987, "M", "驪"), + (0xF988, "M", "麗"), + (0xF989, "M", "黎"), + (0xF98A, "M", "力"), + (0xF98B, "M", "曆"), + (0xF98C, "M", "歷"), + (0xF98D, "M", "轢"), + (0xF98E, "M", "年"), + (0xF98F, "M", "憐"), + (0xF990, "M", "戀"), + (0xF991, "M", "撚"), + (0xF992, "M", "漣"), + (0xF993, "M", "煉"), + (0xF994, "M", "璉"), + (0xF995, "M", "秊"), + (0xF996, "M", "練"), + (0xF997, "M", "聯"), + (0xF998, "M", "輦"), + (0xF999, "M", "蓮"), + (0xF99A, "M", "連"), + (0xF99B, "M", "鍊"), + (0xF99C, "M", "列"), + (0xF99D, "M", "劣"), + (0xF99E, "M", "咽"), + (0xF99F, "M", "烈"), + (0xF9A0, "M", "裂"), + (0xF9A1, "M", "說"), + (0xF9A2, "M", "廉"), + (0xF9A3, "M", "念"), + (0xF9A4, "M", "捻"), + (0xF9A5, "M", "殮"), + (0xF9A6, "M", "簾"), + (0xF9A7, "M", "獵"), + (0xF9A8, "M", "令"), + (0xF9A9, "M", "囹"), + (0xF9AA, "M", "寧"), + (0xF9AB, "M", "嶺"), + (0xF9AC, "M", "怜"), + (0xF9AD, "M", "玲"), + ] + + +def _seg_41() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xF9AE, "M", "瑩"), + (0xF9AF, "M", "羚"), + (0xF9B0, "M", "聆"), + (0xF9B1, "M", "鈴"), + (0xF9B2, "M", "零"), + (0xF9B3, "M", "靈"), + (0xF9B4, "M", "領"), + (0xF9B5, "M", "例"), + (0xF9B6, "M", "禮"), + (0xF9B7, "M", "醴"), + (0xF9B8, "M", "隸"), + (0xF9B9, "M", "惡"), + (0xF9BA, "M", "了"), + (0xF9BB, "M", "僚"), + (0xF9BC, "M", "寮"), + (0xF9BD, "M", "尿"), + (0xF9BE, "M", "料"), + (0xF9BF, "M", "樂"), + (0xF9C0, "M", "燎"), + (0xF9C1, "M", "療"), + (0xF9C2, "M", "蓼"), + (0xF9C3, "M", "遼"), + (0xF9C4, "M", "龍"), + (0xF9C5, "M", "暈"), + (0xF9C6, "M", "阮"), + (0xF9C7, "M", "劉"), + (0xF9C8, "M", "杻"), + (0xF9C9, "M", "柳"), + (0xF9CA, "M", "流"), + (0xF9CB, "M", "溜"), + (0xF9CC, "M", "琉"), + (0xF9CD, "M", "留"), + (0xF9CE, "M", "硫"), + (0xF9CF, "M", "紐"), + (0xF9D0, "M", "類"), + (0xF9D1, "M", "六"), + (0xF9D2, "M", "戮"), + (0xF9D3, "M", "陸"), + (0xF9D4, "M", "倫"), + (0xF9D5, "M", "崙"), + (0xF9D6, "M", "淪"), + (0xF9D7, "M", "輪"), + (0xF9D8, "M", "律"), + (0xF9D9, "M", "慄"), + (0xF9DA, "M", "栗"), + (0xF9DB, "M", "率"), + (0xF9DC, "M", "隆"), + (0xF9DD, "M", "利"), + (0xF9DE, "M", "吏"), + (0xF9DF, "M", "履"), + (0xF9E0, "M", "易"), + (0xF9E1, "M", "李"), + (0xF9E2, "M", "梨"), + (0xF9E3, "M", "泥"), + (0xF9E4, "M", "理"), + (0xF9E5, "M", "痢"), + (0xF9E6, "M", "罹"), + (0xF9E7, "M", "裏"), + (0xF9E8, "M", "裡"), + (0xF9E9, "M", "里"), + (0xF9EA, "M", "離"), + (0xF9EB, "M", "匿"), + (0xF9EC, "M", "溺"), + (0xF9ED, "M", "吝"), + (0xF9EE, "M", "燐"), + (0xF9EF, "M", "璘"), + (0xF9F0, "M", "藺"), + (0xF9F1, "M", "隣"), + (0xF9F2, "M", "鱗"), + (0xF9F3, "M", "麟"), + (0xF9F4, "M", "林"), + (0xF9F5, "M", "淋"), + (0xF9F6, "M", "臨"), + (0xF9F7, "M", "立"), + (0xF9F8, "M", "笠"), + (0xF9F9, "M", "粒"), + (0xF9FA, "M", "狀"), + (0xF9FB, "M", "炙"), + (0xF9FC, "M", "識"), + (0xF9FD, "M", "什"), + (0xF9FE, "M", "茶"), + (0xF9FF, "M", "刺"), + (0xFA00, "M", "切"), + (0xFA01, "M", "度"), + (0xFA02, "M", "拓"), + (0xFA03, "M", "糖"), + (0xFA04, "M", "宅"), + (0xFA05, "M", "洞"), + (0xFA06, "M", "暴"), + (0xFA07, "M", "輻"), + (0xFA08, "M", "行"), + (0xFA09, "M", "降"), + (0xFA0A, "M", "見"), + (0xFA0B, "M", "廓"), + (0xFA0C, "M", "兀"), + (0xFA0D, "M", "嗀"), + (0xFA0E, "V"), + (0xFA10, "M", "塚"), + (0xFA11, "V"), + (0xFA12, "M", "晴"), + ] + + +def _seg_42() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xFA13, "V"), + (0xFA15, "M", "凞"), + (0xFA16, "M", "猪"), + (0xFA17, "M", "益"), + (0xFA18, "M", "礼"), + (0xFA19, "M", "神"), + (0xFA1A, "M", "祥"), + (0xFA1B, "M", "福"), + (0xFA1C, "M", "靖"), + (0xFA1D, "M", "精"), + (0xFA1E, "M", "羽"), + (0xFA1F, "V"), + (0xFA20, "M", "蘒"), + (0xFA21, "V"), + (0xFA22, "M", "諸"), + (0xFA23, "V"), + (0xFA25, "M", "逸"), + (0xFA26, "M", "都"), + (0xFA27, "V"), + (0xFA2A, "M", "飯"), + (0xFA2B, "M", "飼"), + (0xFA2C, "M", "館"), + (0xFA2D, "M", "鶴"), + (0xFA2E, "M", "郞"), + (0xFA2F, "M", "隷"), + (0xFA30, "M", "侮"), + (0xFA31, "M", "僧"), + (0xFA32, "M", "免"), + (0xFA33, "M", "勉"), + (0xFA34, "M", "勤"), + (0xFA35, "M", "卑"), + (0xFA36, "M", "喝"), + (0xFA37, "M", "嘆"), + (0xFA38, "M", "器"), + (0xFA39, "M", "塀"), + (0xFA3A, "M", "墨"), + (0xFA3B, "M", "層"), + (0xFA3C, "M", "屮"), + (0xFA3D, "M", "悔"), + (0xFA3E, "M", "慨"), + (0xFA3F, "M", "憎"), + (0xFA40, "M", "懲"), + (0xFA41, "M", "敏"), + (0xFA42, "M", "既"), + (0xFA43, "M", "暑"), + (0xFA44, "M", "梅"), + (0xFA45, "M", "海"), + (0xFA46, "M", "渚"), + (0xFA47, "M", "漢"), + (0xFA48, "M", "煮"), + (0xFA49, "M", "爫"), + (0xFA4A, "M", "琢"), + (0xFA4B, "M", "碑"), + (0xFA4C, "M", "社"), + (0xFA4D, "M", "祉"), + (0xFA4E, "M", "祈"), + (0xFA4F, "M", "祐"), + (0xFA50, "M", "祖"), + (0xFA51, "M", "祝"), + (0xFA52, "M", "禍"), + (0xFA53, "M", "禎"), + (0xFA54, "M", "穀"), + (0xFA55, "M", "突"), + (0xFA56, "M", "節"), + (0xFA57, "M", "練"), + (0xFA58, "M", "縉"), + (0xFA59, "M", "繁"), + (0xFA5A, "M", "署"), + (0xFA5B, "M", "者"), + (0xFA5C, "M", "臭"), + (0xFA5D, "M", "艹"), + (0xFA5F, "M", "著"), + (0xFA60, "M", "褐"), + (0xFA61, "M", "視"), + (0xFA62, "M", "謁"), + (0xFA63, "M", "謹"), + (0xFA64, "M", "賓"), + (0xFA65, "M", "贈"), + (0xFA66, "M", "辶"), + (0xFA67, "M", "逸"), + (0xFA68, "M", "難"), + (0xFA69, "M", "響"), + (0xFA6A, "M", "頻"), + (0xFA6B, "M", "恵"), + (0xFA6C, "M", "𤋮"), + (0xFA6D, "M", "舘"), + (0xFA6E, "X"), + (0xFA70, "M", "並"), + (0xFA71, "M", "况"), + (0xFA72, "M", "全"), + (0xFA73, "M", "侀"), + (0xFA74, "M", "充"), + (0xFA75, "M", "冀"), + (0xFA76, "M", "勇"), + (0xFA77, "M", "勺"), + (0xFA78, "M", "喝"), + (0xFA79, "M", "啕"), + (0xFA7A, "M", "喙"), + (0xFA7B, "M", "嗢"), + (0xFA7C, "M", "塚"), + ] + + +def _seg_43() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xFA7D, "M", "墳"), + (0xFA7E, "M", "奄"), + (0xFA7F, "M", "奔"), + (0xFA80, "M", "婢"), + (0xFA81, "M", "嬨"), + (0xFA82, "M", "廒"), + (0xFA83, "M", "廙"), + (0xFA84, "M", "彩"), + (0xFA85, "M", "徭"), + (0xFA86, "M", "惘"), + (0xFA87, "M", "慎"), + (0xFA88, "M", "愈"), + (0xFA89, "M", "憎"), + (0xFA8A, "M", "慠"), + (0xFA8B, "M", "懲"), + (0xFA8C, "M", "戴"), + (0xFA8D, "M", "揄"), + (0xFA8E, "M", "搜"), + (0xFA8F, "M", "摒"), + (0xFA90, "M", "敖"), + (0xFA91, "M", "晴"), + (0xFA92, "M", "朗"), + (0xFA93, "M", "望"), + (0xFA94, "M", "杖"), + (0xFA95, "M", "歹"), + (0xFA96, "M", "殺"), + (0xFA97, "M", "流"), + (0xFA98, "M", "滛"), + (0xFA99, "M", "滋"), + (0xFA9A, "M", "漢"), + (0xFA9B, "M", "瀞"), + (0xFA9C, "M", "煮"), + (0xFA9D, "M", "瞧"), + (0xFA9E, "M", "爵"), + (0xFA9F, "M", "犯"), + (0xFAA0, "M", "猪"), + (0xFAA1, "M", "瑱"), + (0xFAA2, "M", "甆"), + (0xFAA3, "M", "画"), + (0xFAA4, "M", "瘝"), + (0xFAA5, "M", "瘟"), + (0xFAA6, "M", "益"), + (0xFAA7, "M", "盛"), + (0xFAA8, "M", "直"), + (0xFAA9, "M", "睊"), + (0xFAAA, "M", "着"), + (0xFAAB, "M", "磌"), + (0xFAAC, "M", "窱"), + (0xFAAD, "M", "節"), + (0xFAAE, "M", "类"), + (0xFAAF, "M", "絛"), + (0xFAB0, "M", "練"), + (0xFAB1, "M", "缾"), + (0xFAB2, "M", "者"), + (0xFAB3, "M", "荒"), + (0xFAB4, "M", "華"), + (0xFAB5, "M", "蝹"), + (0xFAB6, "M", "襁"), + (0xFAB7, "M", "覆"), + (0xFAB8, "M", "視"), + (0xFAB9, "M", "調"), + (0xFABA, "M", "諸"), + (0xFABB, "M", "請"), + (0xFABC, "M", "謁"), + (0xFABD, "M", "諾"), + (0xFABE, "M", "諭"), + (0xFABF, "M", "謹"), + (0xFAC0, "M", "變"), + (0xFAC1, "M", "贈"), + (0xFAC2, "M", "輸"), + (0xFAC3, "M", "遲"), + (0xFAC4, "M", "醙"), + (0xFAC5, "M", "鉶"), + (0xFAC6, "M", "陼"), + (0xFAC7, "M", "難"), + (0xFAC8, "M", "靖"), + (0xFAC9, "M", "韛"), + (0xFACA, "M", "響"), + (0xFACB, "M", "頋"), + (0xFACC, "M", "頻"), + (0xFACD, "M", "鬒"), + (0xFACE, "M", "龜"), + (0xFACF, "M", "𢡊"), + (0xFAD0, "M", "𢡄"), + (0xFAD1, "M", "𣏕"), + (0xFAD2, "M", "㮝"), + (0xFAD3, "M", "䀘"), + (0xFAD4, "M", "䀹"), + (0xFAD5, "M", "𥉉"), + (0xFAD6, "M", "𥳐"), + (0xFAD7, "M", "𧻓"), + (0xFAD8, "M", "齃"), + (0xFAD9, "M", "龎"), + (0xFADA, "X"), + (0xFB00, "M", "ff"), + (0xFB01, "M", "fi"), + (0xFB02, "M", "fl"), + (0xFB03, "M", "ffi"), + (0xFB04, "M", "ffl"), + (0xFB05, "M", "st"), + ] + + +def _seg_44() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xFB07, "X"), + (0xFB13, "M", "մն"), + (0xFB14, "M", "մե"), + (0xFB15, "M", "մի"), + (0xFB16, "M", "վն"), + (0xFB17, "M", "մխ"), + (0xFB18, "X"), + (0xFB1D, "M", "יִ"), + (0xFB1E, "V"), + (0xFB1F, "M", "ײַ"), + (0xFB20, "M", "ע"), + (0xFB21, "M", "א"), + (0xFB22, "M", "ד"), + (0xFB23, "M", "ה"), + (0xFB24, "M", "כ"), + (0xFB25, "M", "ל"), + (0xFB26, "M", "ם"), + (0xFB27, "M", "ר"), + (0xFB28, "M", "ת"), + (0xFB29, "3", "+"), + (0xFB2A, "M", "שׁ"), + (0xFB2B, "M", "שׂ"), + (0xFB2C, "M", "שּׁ"), + (0xFB2D, "M", "שּׂ"), + (0xFB2E, "M", "אַ"), + (0xFB2F, "M", "אָ"), + (0xFB30, "M", "אּ"), + (0xFB31, "M", "בּ"), + (0xFB32, "M", "גּ"), + (0xFB33, "M", "דּ"), + (0xFB34, "M", "הּ"), + (0xFB35, "M", "וּ"), + (0xFB36, "M", "זּ"), + (0xFB37, "X"), + (0xFB38, "M", "טּ"), + (0xFB39, "M", "יּ"), + (0xFB3A, "M", "ךּ"), + (0xFB3B, "M", "כּ"), + (0xFB3C, "M", "לּ"), + (0xFB3D, "X"), + (0xFB3E, "M", "מּ"), + (0xFB3F, "X"), + (0xFB40, "M", "נּ"), + (0xFB41, "M", "סּ"), + (0xFB42, "X"), + (0xFB43, "M", "ףּ"), + (0xFB44, "M", "פּ"), + (0xFB45, "X"), + (0xFB46, "M", "צּ"), + (0xFB47, "M", "קּ"), + (0xFB48, "M", "רּ"), + (0xFB49, "M", "שּ"), + (0xFB4A, "M", "תּ"), + (0xFB4B, "M", "וֹ"), + (0xFB4C, "M", "בֿ"), + (0xFB4D, "M", "כֿ"), + (0xFB4E, "M", "פֿ"), + (0xFB4F, "M", "אל"), + (0xFB50, "M", "ٱ"), + (0xFB52, "M", "ٻ"), + (0xFB56, "M", "پ"), + (0xFB5A, "M", "ڀ"), + (0xFB5E, "M", "ٺ"), + (0xFB62, "M", "ٿ"), + (0xFB66, "M", "ٹ"), + (0xFB6A, "M", "ڤ"), + (0xFB6E, "M", "ڦ"), + (0xFB72, "M", "ڄ"), + (0xFB76, "M", "ڃ"), + (0xFB7A, "M", "چ"), + (0xFB7E, "M", "ڇ"), + (0xFB82, "M", "ڍ"), + (0xFB84, "M", "ڌ"), + (0xFB86, "M", "ڎ"), + (0xFB88, "M", "ڈ"), + (0xFB8A, "M", "ژ"), + (0xFB8C, "M", "ڑ"), + (0xFB8E, "M", "ک"), + (0xFB92, "M", "گ"), + (0xFB96, "M", "ڳ"), + (0xFB9A, "M", "ڱ"), + (0xFB9E, "M", "ں"), + (0xFBA0, "M", "ڻ"), + (0xFBA4, "M", "ۀ"), + (0xFBA6, "M", "ہ"), + (0xFBAA, "M", "ھ"), + (0xFBAE, "M", "ے"), + (0xFBB0, "M", "ۓ"), + (0xFBB2, "V"), + (0xFBC3, "X"), + (0xFBD3, "M", "ڭ"), + (0xFBD7, "M", "ۇ"), + (0xFBD9, "M", "ۆ"), + (0xFBDB, "M", "ۈ"), + (0xFBDD, "M", "ۇٴ"), + (0xFBDE, "M", "ۋ"), + (0xFBE0, "M", "ۅ"), + (0xFBE2, "M", "ۉ"), + (0xFBE4, "M", "ې"), + (0xFBE8, "M", "ى"), + ] + + +def _seg_45() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xFBEA, "M", "ئا"), + (0xFBEC, "M", "ئە"), + (0xFBEE, "M", "ئو"), + (0xFBF0, "M", "ئۇ"), + (0xFBF2, "M", "ئۆ"), + (0xFBF4, "M", "ئۈ"), + (0xFBF6, "M", "ئې"), + (0xFBF9, "M", "ئى"), + (0xFBFC, "M", "ی"), + (0xFC00, "M", "ئج"), + (0xFC01, "M", "ئح"), + (0xFC02, "M", "ئم"), + (0xFC03, "M", "ئى"), + (0xFC04, "M", "ئي"), + (0xFC05, "M", "بج"), + (0xFC06, "M", "بح"), + (0xFC07, "M", "بخ"), + (0xFC08, "M", "بم"), + (0xFC09, "M", "بى"), + (0xFC0A, "M", "بي"), + (0xFC0B, "M", "تج"), + (0xFC0C, "M", "تح"), + (0xFC0D, "M", "تخ"), + (0xFC0E, "M", "تم"), + (0xFC0F, "M", "تى"), + (0xFC10, "M", "تي"), + (0xFC11, "M", "ثج"), + (0xFC12, "M", "ثم"), + (0xFC13, "M", "ثى"), + (0xFC14, "M", "ثي"), + (0xFC15, "M", "جح"), + (0xFC16, "M", "جم"), + (0xFC17, "M", "حج"), + (0xFC18, "M", "حم"), + (0xFC19, "M", "خج"), + (0xFC1A, "M", "خح"), + (0xFC1B, "M", "خم"), + (0xFC1C, "M", "سج"), + (0xFC1D, "M", "سح"), + (0xFC1E, "M", "سخ"), + (0xFC1F, "M", "سم"), + (0xFC20, "M", "صح"), + (0xFC21, "M", "صم"), + (0xFC22, "M", "ضج"), + (0xFC23, "M", "ضح"), + (0xFC24, "M", "ضخ"), + (0xFC25, "M", "ضم"), + (0xFC26, "M", "طح"), + (0xFC27, "M", "طم"), + (0xFC28, "M", "ظم"), + (0xFC29, "M", "عج"), + (0xFC2A, "M", "عم"), + (0xFC2B, "M", "غج"), + (0xFC2C, "M", "غم"), + (0xFC2D, "M", "فج"), + (0xFC2E, "M", "فح"), + (0xFC2F, "M", "فخ"), + (0xFC30, "M", "فم"), + (0xFC31, "M", "فى"), + (0xFC32, "M", "في"), + (0xFC33, "M", "قح"), + (0xFC34, "M", "قم"), + (0xFC35, "M", "قى"), + (0xFC36, "M", "قي"), + (0xFC37, "M", "كا"), + (0xFC38, "M", "كج"), + (0xFC39, "M", "كح"), + (0xFC3A, "M", "كخ"), + (0xFC3B, "M", "كل"), + (0xFC3C, "M", "كم"), + (0xFC3D, "M", "كى"), + (0xFC3E, "M", "كي"), + (0xFC3F, "M", "لج"), + (0xFC40, "M", "لح"), + (0xFC41, "M", "لخ"), + (0xFC42, "M", "لم"), + (0xFC43, "M", "لى"), + (0xFC44, "M", "لي"), + (0xFC45, "M", "مج"), + (0xFC46, "M", "مح"), + (0xFC47, "M", "مخ"), + (0xFC48, "M", "مم"), + (0xFC49, "M", "مى"), + (0xFC4A, "M", "مي"), + (0xFC4B, "M", "نج"), + (0xFC4C, "M", "نح"), + (0xFC4D, "M", "نخ"), + (0xFC4E, "M", "نم"), + (0xFC4F, "M", "نى"), + (0xFC50, "M", "ني"), + (0xFC51, "M", "هج"), + (0xFC52, "M", "هم"), + (0xFC53, "M", "هى"), + (0xFC54, "M", "هي"), + (0xFC55, "M", "يج"), + (0xFC56, "M", "يح"), + (0xFC57, "M", "يخ"), + (0xFC58, "M", "يم"), + (0xFC59, "M", "يى"), + (0xFC5A, "M", "يي"), + ] + + +def _seg_46() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xFC5B, "M", "ذٰ"), + (0xFC5C, "M", "رٰ"), + (0xFC5D, "M", "ىٰ"), + (0xFC5E, "3", " ٌّ"), + (0xFC5F, "3", " ٍّ"), + (0xFC60, "3", " َّ"), + (0xFC61, "3", " ُّ"), + (0xFC62, "3", " ِّ"), + (0xFC63, "3", " ّٰ"), + (0xFC64, "M", "ئر"), + (0xFC65, "M", "ئز"), + (0xFC66, "M", "ئم"), + (0xFC67, "M", "ئن"), + (0xFC68, "M", "ئى"), + (0xFC69, "M", "ئي"), + (0xFC6A, "M", "بر"), + (0xFC6B, "M", "بز"), + (0xFC6C, "M", "بم"), + (0xFC6D, "M", "بن"), + (0xFC6E, "M", "بى"), + (0xFC6F, "M", "بي"), + (0xFC70, "M", "تر"), + (0xFC71, "M", "تز"), + (0xFC72, "M", "تم"), + (0xFC73, "M", "تن"), + (0xFC74, "M", "تى"), + (0xFC75, "M", "تي"), + (0xFC76, "M", "ثر"), + (0xFC77, "M", "ثز"), + (0xFC78, "M", "ثم"), + (0xFC79, "M", "ثن"), + (0xFC7A, "M", "ثى"), + (0xFC7B, "M", "ثي"), + (0xFC7C, "M", "فى"), + (0xFC7D, "M", "في"), + (0xFC7E, "M", "قى"), + (0xFC7F, "M", "قي"), + (0xFC80, "M", "كا"), + (0xFC81, "M", "كل"), + (0xFC82, "M", "كم"), + (0xFC83, "M", "كى"), + (0xFC84, "M", "كي"), + (0xFC85, "M", "لم"), + (0xFC86, "M", "لى"), + (0xFC87, "M", "لي"), + (0xFC88, "M", "ما"), + (0xFC89, "M", "مم"), + (0xFC8A, "M", "نر"), + (0xFC8B, "M", "نز"), + (0xFC8C, "M", "نم"), + (0xFC8D, "M", "نن"), + (0xFC8E, "M", "نى"), + (0xFC8F, "M", "ني"), + (0xFC90, "M", "ىٰ"), + (0xFC91, "M", "ير"), + (0xFC92, "M", "يز"), + (0xFC93, "M", "يم"), + (0xFC94, "M", "ين"), + (0xFC95, "M", "يى"), + (0xFC96, "M", "يي"), + (0xFC97, "M", "ئج"), + (0xFC98, "M", "ئح"), + (0xFC99, "M", "ئخ"), + (0xFC9A, "M", "ئم"), + (0xFC9B, "M", "ئه"), + (0xFC9C, "M", "بج"), + (0xFC9D, "M", "بح"), + (0xFC9E, "M", "بخ"), + (0xFC9F, "M", "بم"), + (0xFCA0, "M", "به"), + (0xFCA1, "M", "تج"), + (0xFCA2, "M", "تح"), + (0xFCA3, "M", "تخ"), + (0xFCA4, "M", "تم"), + (0xFCA5, "M", "ته"), + (0xFCA6, "M", "ثم"), + (0xFCA7, "M", "جح"), + (0xFCA8, "M", "جم"), + (0xFCA9, "M", "حج"), + (0xFCAA, "M", "حم"), + (0xFCAB, "M", "خج"), + (0xFCAC, "M", "خم"), + (0xFCAD, "M", "سج"), + (0xFCAE, "M", "سح"), + (0xFCAF, "M", "سخ"), + (0xFCB0, "M", "سم"), + (0xFCB1, "M", "صح"), + (0xFCB2, "M", "صخ"), + (0xFCB3, "M", "صم"), + (0xFCB4, "M", "ضج"), + (0xFCB5, "M", "ضح"), + (0xFCB6, "M", "ضخ"), + (0xFCB7, "M", "ضم"), + (0xFCB8, "M", "طح"), + (0xFCB9, "M", "ظم"), + (0xFCBA, "M", "عج"), + (0xFCBB, "M", "عم"), + (0xFCBC, "M", "غج"), + (0xFCBD, "M", "غم"), + (0xFCBE, "M", "فج"), + ] + + +def _seg_47() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xFCBF, "M", "فح"), + (0xFCC0, "M", "فخ"), + (0xFCC1, "M", "فم"), + (0xFCC2, "M", "قح"), + (0xFCC3, "M", "قم"), + (0xFCC4, "M", "كج"), + (0xFCC5, "M", "كح"), + (0xFCC6, "M", "كخ"), + (0xFCC7, "M", "كل"), + (0xFCC8, "M", "كم"), + (0xFCC9, "M", "لج"), + (0xFCCA, "M", "لح"), + (0xFCCB, "M", "لخ"), + (0xFCCC, "M", "لم"), + (0xFCCD, "M", "له"), + (0xFCCE, "M", "مج"), + (0xFCCF, "M", "مح"), + (0xFCD0, "M", "مخ"), + (0xFCD1, "M", "مم"), + (0xFCD2, "M", "نج"), + (0xFCD3, "M", "نح"), + (0xFCD4, "M", "نخ"), + (0xFCD5, "M", "نم"), + (0xFCD6, "M", "نه"), + (0xFCD7, "M", "هج"), + (0xFCD8, "M", "هم"), + (0xFCD9, "M", "هٰ"), + (0xFCDA, "M", "يج"), + (0xFCDB, "M", "يح"), + (0xFCDC, "M", "يخ"), + (0xFCDD, "M", "يم"), + (0xFCDE, "M", "يه"), + (0xFCDF, "M", "ئم"), + (0xFCE0, "M", "ئه"), + (0xFCE1, "M", "بم"), + (0xFCE2, "M", "به"), + (0xFCE3, "M", "تم"), + (0xFCE4, "M", "ته"), + (0xFCE5, "M", "ثم"), + (0xFCE6, "M", "ثه"), + (0xFCE7, "M", "سم"), + (0xFCE8, "M", "سه"), + (0xFCE9, "M", "شم"), + (0xFCEA, "M", "شه"), + (0xFCEB, "M", "كل"), + (0xFCEC, "M", "كم"), + (0xFCED, "M", "لم"), + (0xFCEE, "M", "نم"), + (0xFCEF, "M", "نه"), + (0xFCF0, "M", "يم"), + (0xFCF1, "M", "يه"), + (0xFCF2, "M", "ـَّ"), + (0xFCF3, "M", "ـُّ"), + (0xFCF4, "M", "ـِّ"), + (0xFCF5, "M", "طى"), + (0xFCF6, "M", "طي"), + (0xFCF7, "M", "عى"), + (0xFCF8, "M", "عي"), + (0xFCF9, "M", "غى"), + (0xFCFA, "M", "غي"), + (0xFCFB, "M", "سى"), + (0xFCFC, "M", "سي"), + (0xFCFD, "M", "شى"), + (0xFCFE, "M", "شي"), + (0xFCFF, "M", "حى"), + (0xFD00, "M", "حي"), + (0xFD01, "M", "جى"), + (0xFD02, "M", "جي"), + (0xFD03, "M", "خى"), + (0xFD04, "M", "خي"), + (0xFD05, "M", "صى"), + (0xFD06, "M", "صي"), + (0xFD07, "M", "ضى"), + (0xFD08, "M", "ضي"), + (0xFD09, "M", "شج"), + (0xFD0A, "M", "شح"), + (0xFD0B, "M", "شخ"), + (0xFD0C, "M", "شم"), + (0xFD0D, "M", "شر"), + (0xFD0E, "M", "سر"), + (0xFD0F, "M", "صر"), + (0xFD10, "M", "ضر"), + (0xFD11, "M", "طى"), + (0xFD12, "M", "طي"), + (0xFD13, "M", "عى"), + (0xFD14, "M", "عي"), + (0xFD15, "M", "غى"), + (0xFD16, "M", "غي"), + (0xFD17, "M", "سى"), + (0xFD18, "M", "سي"), + (0xFD19, "M", "شى"), + (0xFD1A, "M", "شي"), + (0xFD1B, "M", "حى"), + (0xFD1C, "M", "حي"), + (0xFD1D, "M", "جى"), + (0xFD1E, "M", "جي"), + (0xFD1F, "M", "خى"), + (0xFD20, "M", "خي"), + (0xFD21, "M", "صى"), + (0xFD22, "M", "صي"), + ] + + +def _seg_48() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xFD23, "M", "ضى"), + (0xFD24, "M", "ضي"), + (0xFD25, "M", "شج"), + (0xFD26, "M", "شح"), + (0xFD27, "M", "شخ"), + (0xFD28, "M", "شم"), + (0xFD29, "M", "شر"), + (0xFD2A, "M", "سر"), + (0xFD2B, "M", "صر"), + (0xFD2C, "M", "ضر"), + (0xFD2D, "M", "شج"), + (0xFD2E, "M", "شح"), + (0xFD2F, "M", "شخ"), + (0xFD30, "M", "شم"), + (0xFD31, "M", "سه"), + (0xFD32, "M", "شه"), + (0xFD33, "M", "طم"), + (0xFD34, "M", "سج"), + (0xFD35, "M", "سح"), + (0xFD36, "M", "سخ"), + (0xFD37, "M", "شج"), + (0xFD38, "M", "شح"), + (0xFD39, "M", "شخ"), + (0xFD3A, "M", "طم"), + (0xFD3B, "M", "ظم"), + (0xFD3C, "M", "اً"), + (0xFD3E, "V"), + (0xFD50, "M", "تجم"), + (0xFD51, "M", "تحج"), + (0xFD53, "M", "تحم"), + (0xFD54, "M", "تخم"), + (0xFD55, "M", "تمج"), + (0xFD56, "M", "تمح"), + (0xFD57, "M", "تمخ"), + (0xFD58, "M", "جمح"), + (0xFD5A, "M", "حمي"), + (0xFD5B, "M", "حمى"), + (0xFD5C, "M", "سحج"), + (0xFD5D, "M", "سجح"), + (0xFD5E, "M", "سجى"), + (0xFD5F, "M", "سمح"), + (0xFD61, "M", "سمج"), + (0xFD62, "M", "سمم"), + (0xFD64, "M", "صحح"), + (0xFD66, "M", "صمم"), + (0xFD67, "M", "شحم"), + (0xFD69, "M", "شجي"), + (0xFD6A, "M", "شمخ"), + (0xFD6C, "M", "شمم"), + (0xFD6E, "M", "ضحى"), + (0xFD6F, "M", "ضخم"), + (0xFD71, "M", "طمح"), + (0xFD73, "M", "طمم"), + (0xFD74, "M", "طمي"), + (0xFD75, "M", "عجم"), + (0xFD76, "M", "عمم"), + (0xFD78, "M", "عمى"), + (0xFD79, "M", "غمم"), + (0xFD7A, "M", "غمي"), + (0xFD7B, "M", "غمى"), + (0xFD7C, "M", "فخم"), + (0xFD7E, "M", "قمح"), + (0xFD7F, "M", "قمم"), + (0xFD80, "M", "لحم"), + (0xFD81, "M", "لحي"), + (0xFD82, "M", "لحى"), + (0xFD83, "M", "لجج"), + (0xFD85, "M", "لخم"), + (0xFD87, "M", "لمح"), + (0xFD89, "M", "محج"), + (0xFD8A, "M", "محم"), + (0xFD8B, "M", "محي"), + (0xFD8C, "M", "مجح"), + (0xFD8D, "M", "مجم"), + (0xFD8E, "M", "مخج"), + (0xFD8F, "M", "مخم"), + (0xFD90, "X"), + (0xFD92, "M", "مجخ"), + (0xFD93, "M", "همج"), + (0xFD94, "M", "همم"), + (0xFD95, "M", "نحم"), + (0xFD96, "M", "نحى"), + (0xFD97, "M", "نجم"), + (0xFD99, "M", "نجى"), + (0xFD9A, "M", "نمي"), + (0xFD9B, "M", "نمى"), + (0xFD9C, "M", "يمم"), + (0xFD9E, "M", "بخي"), + (0xFD9F, "M", "تجي"), + (0xFDA0, "M", "تجى"), + (0xFDA1, "M", "تخي"), + (0xFDA2, "M", "تخى"), + (0xFDA3, "M", "تمي"), + (0xFDA4, "M", "تمى"), + (0xFDA5, "M", "جمي"), + (0xFDA6, "M", "جحى"), + (0xFDA7, "M", "جمى"), + (0xFDA8, "M", "سخى"), + (0xFDA9, "M", "صحي"), + (0xFDAA, "M", "شحي"), + ] + + +def _seg_49() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xFDAB, "M", "ضحي"), + (0xFDAC, "M", "لجي"), + (0xFDAD, "M", "لمي"), + (0xFDAE, "M", "يحي"), + (0xFDAF, "M", "يجي"), + (0xFDB0, "M", "يمي"), + (0xFDB1, "M", "ممي"), + (0xFDB2, "M", "قمي"), + (0xFDB3, "M", "نحي"), + (0xFDB4, "M", "قمح"), + (0xFDB5, "M", "لحم"), + (0xFDB6, "M", "عمي"), + (0xFDB7, "M", "كمي"), + (0xFDB8, "M", "نجح"), + (0xFDB9, "M", "مخي"), + (0xFDBA, "M", "لجم"), + (0xFDBB, "M", "كمم"), + (0xFDBC, "M", "لجم"), + (0xFDBD, "M", "نجح"), + (0xFDBE, "M", "جحي"), + (0xFDBF, "M", "حجي"), + (0xFDC0, "M", "مجي"), + (0xFDC1, "M", "فمي"), + (0xFDC2, "M", "بحي"), + (0xFDC3, "M", "كمم"), + (0xFDC4, "M", "عجم"), + (0xFDC5, "M", "صمم"), + (0xFDC6, "M", "سخي"), + (0xFDC7, "M", "نجي"), + (0xFDC8, "X"), + (0xFDCF, "V"), + (0xFDD0, "X"), + (0xFDF0, "M", "صلے"), + (0xFDF1, "M", "قلے"), + (0xFDF2, "M", "الله"), + (0xFDF3, "M", "اكبر"), + (0xFDF4, "M", "محمد"), + (0xFDF5, "M", "صلعم"), + (0xFDF6, "M", "رسول"), + (0xFDF7, "M", "عليه"), + (0xFDF8, "M", "وسلم"), + (0xFDF9, "M", "صلى"), + (0xFDFA, "3", "صلى الله عليه وسلم"), + (0xFDFB, "3", "جل جلاله"), + (0xFDFC, "M", "ریال"), + (0xFDFD, "V"), + (0xFE00, "I"), + (0xFE10, "3", ","), + (0xFE11, "M", "、"), + (0xFE12, "X"), + (0xFE13, "3", ":"), + (0xFE14, "3", ";"), + (0xFE15, "3", "!"), + (0xFE16, "3", "?"), + (0xFE17, "M", "〖"), + (0xFE18, "M", "〗"), + (0xFE19, "X"), + (0xFE20, "V"), + (0xFE30, "X"), + (0xFE31, "M", "—"), + (0xFE32, "M", "–"), + (0xFE33, "3", "_"), + (0xFE35, "3", "("), + (0xFE36, "3", ")"), + (0xFE37, "3", "{"), + (0xFE38, "3", "}"), + (0xFE39, "M", "〔"), + (0xFE3A, "M", "〕"), + (0xFE3B, "M", "【"), + (0xFE3C, "M", "】"), + (0xFE3D, "M", "《"), + (0xFE3E, "M", "》"), + (0xFE3F, "M", "〈"), + (0xFE40, "M", "〉"), + (0xFE41, "M", "「"), + (0xFE42, "M", "」"), + (0xFE43, "M", "『"), + (0xFE44, "M", "』"), + (0xFE45, "V"), + (0xFE47, "3", "["), + (0xFE48, "3", "]"), + (0xFE49, "3", " ̅"), + (0xFE4D, "3", "_"), + (0xFE50, "3", ","), + (0xFE51, "M", "、"), + (0xFE52, "X"), + (0xFE54, "3", ";"), + (0xFE55, "3", ":"), + (0xFE56, "3", "?"), + (0xFE57, "3", "!"), + (0xFE58, "M", "—"), + (0xFE59, "3", "("), + (0xFE5A, "3", ")"), + (0xFE5B, "3", "{"), + (0xFE5C, "3", "}"), + (0xFE5D, "M", "〔"), + (0xFE5E, "M", "〕"), + (0xFE5F, "3", "#"), + (0xFE60, "3", "&"), + (0xFE61, "3", "*"), + ] + + +def _seg_50() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xFE62, "3", "+"), + (0xFE63, "M", "-"), + (0xFE64, "3", "<"), + (0xFE65, "3", ">"), + (0xFE66, "3", "="), + (0xFE67, "X"), + (0xFE68, "3", "\\"), + (0xFE69, "3", "$"), + (0xFE6A, "3", "%"), + (0xFE6B, "3", "@"), + (0xFE6C, "X"), + (0xFE70, "3", " ً"), + (0xFE71, "M", "ـً"), + (0xFE72, "3", " ٌ"), + (0xFE73, "V"), + (0xFE74, "3", " ٍ"), + (0xFE75, "X"), + (0xFE76, "3", " َ"), + (0xFE77, "M", "ـَ"), + (0xFE78, "3", " ُ"), + (0xFE79, "M", "ـُ"), + (0xFE7A, "3", " ِ"), + (0xFE7B, "M", "ـِ"), + (0xFE7C, "3", " ّ"), + (0xFE7D, "M", "ـّ"), + (0xFE7E, "3", " ْ"), + (0xFE7F, "M", "ـْ"), + (0xFE80, "M", "ء"), + (0xFE81, "M", "آ"), + (0xFE83, "M", "أ"), + (0xFE85, "M", "ؤ"), + (0xFE87, "M", "إ"), + (0xFE89, "M", "ئ"), + (0xFE8D, "M", "ا"), + (0xFE8F, "M", "ب"), + (0xFE93, "M", "ة"), + (0xFE95, "M", "ت"), + (0xFE99, "M", "ث"), + (0xFE9D, "M", "ج"), + (0xFEA1, "M", "ح"), + (0xFEA5, "M", "خ"), + (0xFEA9, "M", "د"), + (0xFEAB, "M", "ذ"), + (0xFEAD, "M", "ر"), + (0xFEAF, "M", "ز"), + (0xFEB1, "M", "س"), + (0xFEB5, "M", "ش"), + (0xFEB9, "M", "ص"), + (0xFEBD, "M", "ض"), + (0xFEC1, "M", "ط"), + (0xFEC5, "M", "ظ"), + (0xFEC9, "M", "ع"), + (0xFECD, "M", "غ"), + (0xFED1, "M", "ف"), + (0xFED5, "M", "ق"), + (0xFED9, "M", "ك"), + (0xFEDD, "M", "ل"), + (0xFEE1, "M", "م"), + (0xFEE5, "M", "ن"), + (0xFEE9, "M", "ه"), + (0xFEED, "M", "و"), + (0xFEEF, "M", "ى"), + (0xFEF1, "M", "ي"), + (0xFEF5, "M", "لآ"), + (0xFEF7, "M", "لأ"), + (0xFEF9, "M", "لإ"), + (0xFEFB, "M", "لا"), + (0xFEFD, "X"), + (0xFEFF, "I"), + (0xFF00, "X"), + (0xFF01, "3", "!"), + (0xFF02, "3", '"'), + (0xFF03, "3", "#"), + (0xFF04, "3", "$"), + (0xFF05, "3", "%"), + (0xFF06, "3", "&"), + (0xFF07, "3", "'"), + (0xFF08, "3", "("), + (0xFF09, "3", ")"), + (0xFF0A, "3", "*"), + (0xFF0B, "3", "+"), + (0xFF0C, "3", ","), + (0xFF0D, "M", "-"), + (0xFF0E, "M", "."), + (0xFF0F, "3", "/"), + (0xFF10, "M", "0"), + (0xFF11, "M", "1"), + (0xFF12, "M", "2"), + (0xFF13, "M", "3"), + (0xFF14, "M", "4"), + (0xFF15, "M", "5"), + (0xFF16, "M", "6"), + (0xFF17, "M", "7"), + (0xFF18, "M", "8"), + (0xFF19, "M", "9"), + (0xFF1A, "3", ":"), + (0xFF1B, "3", ";"), + (0xFF1C, "3", "<"), + (0xFF1D, "3", "="), + (0xFF1E, "3", ">"), + ] + + +def _seg_51() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xFF1F, "3", "?"), + (0xFF20, "3", "@"), + (0xFF21, "M", "a"), + (0xFF22, "M", "b"), + (0xFF23, "M", "c"), + (0xFF24, "M", "d"), + (0xFF25, "M", "e"), + (0xFF26, "M", "f"), + (0xFF27, "M", "g"), + (0xFF28, "M", "h"), + (0xFF29, "M", "i"), + (0xFF2A, "M", "j"), + (0xFF2B, "M", "k"), + (0xFF2C, "M", "l"), + (0xFF2D, "M", "m"), + (0xFF2E, "M", "n"), + (0xFF2F, "M", "o"), + (0xFF30, "M", "p"), + (0xFF31, "M", "q"), + (0xFF32, "M", "r"), + (0xFF33, "M", "s"), + (0xFF34, "M", "t"), + (0xFF35, "M", "u"), + (0xFF36, "M", "v"), + (0xFF37, "M", "w"), + (0xFF38, "M", "x"), + (0xFF39, "M", "y"), + (0xFF3A, "M", "z"), + (0xFF3B, "3", "["), + (0xFF3C, "3", "\\"), + (0xFF3D, "3", "]"), + (0xFF3E, "3", "^"), + (0xFF3F, "3", "_"), + (0xFF40, "3", "`"), + (0xFF41, "M", "a"), + (0xFF42, "M", "b"), + (0xFF43, "M", "c"), + (0xFF44, "M", "d"), + (0xFF45, "M", "e"), + (0xFF46, "M", "f"), + (0xFF47, "M", "g"), + (0xFF48, "M", "h"), + (0xFF49, "M", "i"), + (0xFF4A, "M", "j"), + (0xFF4B, "M", "k"), + (0xFF4C, "M", "l"), + (0xFF4D, "M", "m"), + (0xFF4E, "M", "n"), + (0xFF4F, "M", "o"), + (0xFF50, "M", "p"), + (0xFF51, "M", "q"), + (0xFF52, "M", "r"), + (0xFF53, "M", "s"), + (0xFF54, "M", "t"), + (0xFF55, "M", "u"), + (0xFF56, "M", "v"), + (0xFF57, "M", "w"), + (0xFF58, "M", "x"), + (0xFF59, "M", "y"), + (0xFF5A, "M", "z"), + (0xFF5B, "3", "{"), + (0xFF5C, "3", "|"), + (0xFF5D, "3", "}"), + (0xFF5E, "3", "~"), + (0xFF5F, "M", "⦅"), + (0xFF60, "M", "⦆"), + (0xFF61, "M", "."), + (0xFF62, "M", "「"), + (0xFF63, "M", "」"), + (0xFF64, "M", "、"), + (0xFF65, "M", "・"), + (0xFF66, "M", "ヲ"), + (0xFF67, "M", "ァ"), + (0xFF68, "M", "ィ"), + (0xFF69, "M", "ゥ"), + (0xFF6A, "M", "ェ"), + (0xFF6B, "M", "ォ"), + (0xFF6C, "M", "ャ"), + (0xFF6D, "M", "ュ"), + (0xFF6E, "M", "ョ"), + (0xFF6F, "M", "ッ"), + (0xFF70, "M", "ー"), + (0xFF71, "M", "ア"), + (0xFF72, "M", "イ"), + (0xFF73, "M", "ウ"), + (0xFF74, "M", "エ"), + (0xFF75, "M", "オ"), + (0xFF76, "M", "カ"), + (0xFF77, "M", "キ"), + (0xFF78, "M", "ク"), + (0xFF79, "M", "ケ"), + (0xFF7A, "M", "コ"), + (0xFF7B, "M", "サ"), + (0xFF7C, "M", "シ"), + (0xFF7D, "M", "ス"), + (0xFF7E, "M", "セ"), + (0xFF7F, "M", "ソ"), + (0xFF80, "M", "タ"), + (0xFF81, "M", "チ"), + (0xFF82, "M", "ツ"), + ] + + +def _seg_52() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xFF83, "M", "テ"), + (0xFF84, "M", "ト"), + (0xFF85, "M", "ナ"), + (0xFF86, "M", "ニ"), + (0xFF87, "M", "ヌ"), + (0xFF88, "M", "ネ"), + (0xFF89, "M", "ノ"), + (0xFF8A, "M", "ハ"), + (0xFF8B, "M", "ヒ"), + (0xFF8C, "M", "フ"), + (0xFF8D, "M", "ヘ"), + (0xFF8E, "M", "ホ"), + (0xFF8F, "M", "マ"), + (0xFF90, "M", "ミ"), + (0xFF91, "M", "ム"), + (0xFF92, "M", "メ"), + (0xFF93, "M", "モ"), + (0xFF94, "M", "ヤ"), + (0xFF95, "M", "ユ"), + (0xFF96, "M", "ヨ"), + (0xFF97, "M", "ラ"), + (0xFF98, "M", "リ"), + (0xFF99, "M", "ル"), + (0xFF9A, "M", "レ"), + (0xFF9B, "M", "ロ"), + (0xFF9C, "M", "ワ"), + (0xFF9D, "M", "ン"), + (0xFF9E, "M", "゙"), + (0xFF9F, "M", "゚"), + (0xFFA0, "X"), + (0xFFA1, "M", "ᄀ"), + (0xFFA2, "M", "ᄁ"), + (0xFFA3, "M", "ᆪ"), + (0xFFA4, "M", "ᄂ"), + (0xFFA5, "M", "ᆬ"), + (0xFFA6, "M", "ᆭ"), + (0xFFA7, "M", "ᄃ"), + (0xFFA8, "M", "ᄄ"), + (0xFFA9, "M", "ᄅ"), + (0xFFAA, "M", "ᆰ"), + (0xFFAB, "M", "ᆱ"), + (0xFFAC, "M", "ᆲ"), + (0xFFAD, "M", "ᆳ"), + (0xFFAE, "M", "ᆴ"), + (0xFFAF, "M", "ᆵ"), + (0xFFB0, "M", "ᄚ"), + (0xFFB1, "M", "ᄆ"), + (0xFFB2, "M", "ᄇ"), + (0xFFB3, "M", "ᄈ"), + (0xFFB4, "M", "ᄡ"), + (0xFFB5, "M", "ᄉ"), + (0xFFB6, "M", "ᄊ"), + (0xFFB7, "M", "ᄋ"), + (0xFFB8, "M", "ᄌ"), + (0xFFB9, "M", "ᄍ"), + (0xFFBA, "M", "ᄎ"), + (0xFFBB, "M", "ᄏ"), + (0xFFBC, "M", "ᄐ"), + (0xFFBD, "M", "ᄑ"), + (0xFFBE, "M", "ᄒ"), + (0xFFBF, "X"), + (0xFFC2, "M", "ᅡ"), + (0xFFC3, "M", "ᅢ"), + (0xFFC4, "M", "ᅣ"), + (0xFFC5, "M", "ᅤ"), + (0xFFC6, "M", "ᅥ"), + (0xFFC7, "M", "ᅦ"), + (0xFFC8, "X"), + (0xFFCA, "M", "ᅧ"), + (0xFFCB, "M", "ᅨ"), + (0xFFCC, "M", "ᅩ"), + (0xFFCD, "M", "ᅪ"), + (0xFFCE, "M", "ᅫ"), + (0xFFCF, "M", "ᅬ"), + (0xFFD0, "X"), + (0xFFD2, "M", "ᅭ"), + (0xFFD3, "M", "ᅮ"), + (0xFFD4, "M", "ᅯ"), + (0xFFD5, "M", "ᅰ"), + (0xFFD6, "M", "ᅱ"), + (0xFFD7, "M", "ᅲ"), + (0xFFD8, "X"), + (0xFFDA, "M", "ᅳ"), + (0xFFDB, "M", "ᅴ"), + (0xFFDC, "M", "ᅵ"), + (0xFFDD, "X"), + (0xFFE0, "M", "¢"), + (0xFFE1, "M", "£"), + (0xFFE2, "M", "¬"), + (0xFFE3, "3", " ̄"), + (0xFFE4, "M", "¦"), + (0xFFE5, "M", "¥"), + (0xFFE6, "M", "₩"), + (0xFFE7, "X"), + (0xFFE8, "M", "│"), + (0xFFE9, "M", "←"), + (0xFFEA, "M", "↑"), + (0xFFEB, "M", "→"), + (0xFFEC, "M", "↓"), + (0xFFED, "M", "■"), + ] + + +def _seg_53() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xFFEE, "M", "○"), + (0xFFEF, "X"), + (0x10000, "V"), + (0x1000C, "X"), + (0x1000D, "V"), + (0x10027, "X"), + (0x10028, "V"), + (0x1003B, "X"), + (0x1003C, "V"), + (0x1003E, "X"), + (0x1003F, "V"), + (0x1004E, "X"), + (0x10050, "V"), + (0x1005E, "X"), + (0x10080, "V"), + (0x100FB, "X"), + (0x10100, "V"), + (0x10103, "X"), + (0x10107, "V"), + (0x10134, "X"), + (0x10137, "V"), + (0x1018F, "X"), + (0x10190, "V"), + (0x1019D, "X"), + (0x101A0, "V"), + (0x101A1, "X"), + (0x101D0, "V"), + (0x101FE, "X"), + (0x10280, "V"), + (0x1029D, "X"), + (0x102A0, "V"), + (0x102D1, "X"), + (0x102E0, "V"), + (0x102FC, "X"), + (0x10300, "V"), + (0x10324, "X"), + (0x1032D, "V"), + (0x1034B, "X"), + (0x10350, "V"), + (0x1037B, "X"), + (0x10380, "V"), + (0x1039E, "X"), + (0x1039F, "V"), + (0x103C4, "X"), + (0x103C8, "V"), + (0x103D6, "X"), + (0x10400, "M", "𐐨"), + (0x10401, "M", "𐐩"), + (0x10402, "M", "𐐪"), + (0x10403, "M", "𐐫"), + (0x10404, "M", "𐐬"), + (0x10405, "M", "𐐭"), + (0x10406, "M", "𐐮"), + (0x10407, "M", "𐐯"), + (0x10408, "M", "𐐰"), + (0x10409, "M", "𐐱"), + (0x1040A, "M", "𐐲"), + (0x1040B, "M", "𐐳"), + (0x1040C, "M", "𐐴"), + (0x1040D, "M", "𐐵"), + (0x1040E, "M", "𐐶"), + (0x1040F, "M", "𐐷"), + (0x10410, "M", "𐐸"), + (0x10411, "M", "𐐹"), + (0x10412, "M", "𐐺"), + (0x10413, "M", "𐐻"), + (0x10414, "M", "𐐼"), + (0x10415, "M", "𐐽"), + (0x10416, "M", "𐐾"), + (0x10417, "M", "𐐿"), + (0x10418, "M", "𐑀"), + (0x10419, "M", "𐑁"), + (0x1041A, "M", "𐑂"), + (0x1041B, "M", "𐑃"), + (0x1041C, "M", "𐑄"), + (0x1041D, "M", "𐑅"), + (0x1041E, "M", "𐑆"), + (0x1041F, "M", "𐑇"), + (0x10420, "M", "𐑈"), + (0x10421, "M", "𐑉"), + (0x10422, "M", "𐑊"), + (0x10423, "M", "𐑋"), + (0x10424, "M", "𐑌"), + (0x10425, "M", "𐑍"), + (0x10426, "M", "𐑎"), + (0x10427, "M", "𐑏"), + (0x10428, "V"), + (0x1049E, "X"), + (0x104A0, "V"), + (0x104AA, "X"), + (0x104B0, "M", "𐓘"), + (0x104B1, "M", "𐓙"), + (0x104B2, "M", "𐓚"), + (0x104B3, "M", "𐓛"), + (0x104B4, "M", "𐓜"), + (0x104B5, "M", "𐓝"), + (0x104B6, "M", "𐓞"), + (0x104B7, "M", "𐓟"), + (0x104B8, "M", "𐓠"), + (0x104B9, "M", "𐓡"), + ] + + +def _seg_54() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x104BA, "M", "𐓢"), + (0x104BB, "M", "𐓣"), + (0x104BC, "M", "𐓤"), + (0x104BD, "M", "𐓥"), + (0x104BE, "M", "𐓦"), + (0x104BF, "M", "𐓧"), + (0x104C0, "M", "𐓨"), + (0x104C1, "M", "𐓩"), + (0x104C2, "M", "𐓪"), + (0x104C3, "M", "𐓫"), + (0x104C4, "M", "𐓬"), + (0x104C5, "M", "𐓭"), + (0x104C6, "M", "𐓮"), + (0x104C7, "M", "𐓯"), + (0x104C8, "M", "𐓰"), + (0x104C9, "M", "𐓱"), + (0x104CA, "M", "𐓲"), + (0x104CB, "M", "𐓳"), + (0x104CC, "M", "𐓴"), + (0x104CD, "M", "𐓵"), + (0x104CE, "M", "𐓶"), + (0x104CF, "M", "𐓷"), + (0x104D0, "M", "𐓸"), + (0x104D1, "M", "𐓹"), + (0x104D2, "M", "𐓺"), + (0x104D3, "M", "𐓻"), + (0x104D4, "X"), + (0x104D8, "V"), + (0x104FC, "X"), + (0x10500, "V"), + (0x10528, "X"), + (0x10530, "V"), + (0x10564, "X"), + (0x1056F, "V"), + (0x10570, "M", "𐖗"), + (0x10571, "M", "𐖘"), + (0x10572, "M", "𐖙"), + (0x10573, "M", "𐖚"), + (0x10574, "M", "𐖛"), + (0x10575, "M", "𐖜"), + (0x10576, "M", "𐖝"), + (0x10577, "M", "𐖞"), + (0x10578, "M", "𐖟"), + (0x10579, "M", "𐖠"), + (0x1057A, "M", "𐖡"), + (0x1057B, "X"), + (0x1057C, "M", "𐖣"), + (0x1057D, "M", "𐖤"), + (0x1057E, "M", "𐖥"), + (0x1057F, "M", "𐖦"), + (0x10580, "M", "𐖧"), + (0x10581, "M", "𐖨"), + (0x10582, "M", "𐖩"), + (0x10583, "M", "𐖪"), + (0x10584, "M", "𐖫"), + (0x10585, "M", "𐖬"), + (0x10586, "M", "𐖭"), + (0x10587, "M", "𐖮"), + (0x10588, "M", "𐖯"), + (0x10589, "M", "𐖰"), + (0x1058A, "M", "𐖱"), + (0x1058B, "X"), + (0x1058C, "M", "𐖳"), + (0x1058D, "M", "𐖴"), + (0x1058E, "M", "𐖵"), + (0x1058F, "M", "𐖶"), + (0x10590, "M", "𐖷"), + (0x10591, "M", "𐖸"), + (0x10592, "M", "𐖹"), + (0x10593, "X"), + (0x10594, "M", "𐖻"), + (0x10595, "M", "𐖼"), + (0x10596, "X"), + (0x10597, "V"), + (0x105A2, "X"), + (0x105A3, "V"), + (0x105B2, "X"), + (0x105B3, "V"), + (0x105BA, "X"), + (0x105BB, "V"), + (0x105BD, "X"), + (0x10600, "V"), + (0x10737, "X"), + (0x10740, "V"), + (0x10756, "X"), + (0x10760, "V"), + (0x10768, "X"), + (0x10780, "V"), + (0x10781, "M", "ː"), + (0x10782, "M", "ˑ"), + (0x10783, "M", "æ"), + (0x10784, "M", "ʙ"), + (0x10785, "M", "ɓ"), + (0x10786, "X"), + (0x10787, "M", "ʣ"), + (0x10788, "M", "ꭦ"), + (0x10789, "M", "ʥ"), + (0x1078A, "M", "ʤ"), + (0x1078B, "M", "ɖ"), + (0x1078C, "M", "ɗ"), + ] + + +def _seg_55() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1078D, "M", "ᶑ"), + (0x1078E, "M", "ɘ"), + (0x1078F, "M", "ɞ"), + (0x10790, "M", "ʩ"), + (0x10791, "M", "ɤ"), + (0x10792, "M", "ɢ"), + (0x10793, "M", "ɠ"), + (0x10794, "M", "ʛ"), + (0x10795, "M", "ħ"), + (0x10796, "M", "ʜ"), + (0x10797, "M", "ɧ"), + (0x10798, "M", "ʄ"), + (0x10799, "M", "ʪ"), + (0x1079A, "M", "ʫ"), + (0x1079B, "M", "ɬ"), + (0x1079C, "M", "𝼄"), + (0x1079D, "M", "ꞎ"), + (0x1079E, "M", "ɮ"), + (0x1079F, "M", "𝼅"), + (0x107A0, "M", "ʎ"), + (0x107A1, "M", "𝼆"), + (0x107A2, "M", "ø"), + (0x107A3, "M", "ɶ"), + (0x107A4, "M", "ɷ"), + (0x107A5, "M", "q"), + (0x107A6, "M", "ɺ"), + (0x107A7, "M", "𝼈"), + (0x107A8, "M", "ɽ"), + (0x107A9, "M", "ɾ"), + (0x107AA, "M", "ʀ"), + (0x107AB, "M", "ʨ"), + (0x107AC, "M", "ʦ"), + (0x107AD, "M", "ꭧ"), + (0x107AE, "M", "ʧ"), + (0x107AF, "M", "ʈ"), + (0x107B0, "M", "ⱱ"), + (0x107B1, "X"), + (0x107B2, "M", "ʏ"), + (0x107B3, "M", "ʡ"), + (0x107B4, "M", "ʢ"), + (0x107B5, "M", "ʘ"), + (0x107B6, "M", "ǀ"), + (0x107B7, "M", "ǁ"), + (0x107B8, "M", "ǂ"), + (0x107B9, "M", "𝼊"), + (0x107BA, "M", "𝼞"), + (0x107BB, "X"), + (0x10800, "V"), + (0x10806, "X"), + (0x10808, "V"), + (0x10809, "X"), + (0x1080A, "V"), + (0x10836, "X"), + (0x10837, "V"), + (0x10839, "X"), + (0x1083C, "V"), + (0x1083D, "X"), + (0x1083F, "V"), + (0x10856, "X"), + (0x10857, "V"), + (0x1089F, "X"), + (0x108A7, "V"), + (0x108B0, "X"), + (0x108E0, "V"), + (0x108F3, "X"), + (0x108F4, "V"), + (0x108F6, "X"), + (0x108FB, "V"), + (0x1091C, "X"), + (0x1091F, "V"), + (0x1093A, "X"), + (0x1093F, "V"), + (0x10940, "X"), + (0x10980, "V"), + (0x109B8, "X"), + (0x109BC, "V"), + (0x109D0, "X"), + (0x109D2, "V"), + (0x10A04, "X"), + (0x10A05, "V"), + (0x10A07, "X"), + (0x10A0C, "V"), + (0x10A14, "X"), + (0x10A15, "V"), + (0x10A18, "X"), + (0x10A19, "V"), + (0x10A36, "X"), + (0x10A38, "V"), + (0x10A3B, "X"), + (0x10A3F, "V"), + (0x10A49, "X"), + (0x10A50, "V"), + (0x10A59, "X"), + (0x10A60, "V"), + (0x10AA0, "X"), + (0x10AC0, "V"), + (0x10AE7, "X"), + (0x10AEB, "V"), + (0x10AF7, "X"), + (0x10B00, "V"), + ] + + +def _seg_56() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x10B36, "X"), + (0x10B39, "V"), + (0x10B56, "X"), + (0x10B58, "V"), + (0x10B73, "X"), + (0x10B78, "V"), + (0x10B92, "X"), + (0x10B99, "V"), + (0x10B9D, "X"), + (0x10BA9, "V"), + (0x10BB0, "X"), + (0x10C00, "V"), + (0x10C49, "X"), + (0x10C80, "M", "𐳀"), + (0x10C81, "M", "𐳁"), + (0x10C82, "M", "𐳂"), + (0x10C83, "M", "𐳃"), + (0x10C84, "M", "𐳄"), + (0x10C85, "M", "𐳅"), + (0x10C86, "M", "𐳆"), + (0x10C87, "M", "𐳇"), + (0x10C88, "M", "𐳈"), + (0x10C89, "M", "𐳉"), + (0x10C8A, "M", "𐳊"), + (0x10C8B, "M", "𐳋"), + (0x10C8C, "M", "𐳌"), + (0x10C8D, "M", "𐳍"), + (0x10C8E, "M", "𐳎"), + (0x10C8F, "M", "𐳏"), + (0x10C90, "M", "𐳐"), + (0x10C91, "M", "𐳑"), + (0x10C92, "M", "𐳒"), + (0x10C93, "M", "𐳓"), + (0x10C94, "M", "𐳔"), + (0x10C95, "M", "𐳕"), + (0x10C96, "M", "𐳖"), + (0x10C97, "M", "𐳗"), + (0x10C98, "M", "𐳘"), + (0x10C99, "M", "𐳙"), + (0x10C9A, "M", "𐳚"), + (0x10C9B, "M", "𐳛"), + (0x10C9C, "M", "𐳜"), + (0x10C9D, "M", "𐳝"), + (0x10C9E, "M", "𐳞"), + (0x10C9F, "M", "𐳟"), + (0x10CA0, "M", "𐳠"), + (0x10CA1, "M", "𐳡"), + (0x10CA2, "M", "𐳢"), + (0x10CA3, "M", "𐳣"), + (0x10CA4, "M", "𐳤"), + (0x10CA5, "M", "𐳥"), + (0x10CA6, "M", "𐳦"), + (0x10CA7, "M", "𐳧"), + (0x10CA8, "M", "𐳨"), + (0x10CA9, "M", "𐳩"), + (0x10CAA, "M", "𐳪"), + (0x10CAB, "M", "𐳫"), + (0x10CAC, "M", "𐳬"), + (0x10CAD, "M", "𐳭"), + (0x10CAE, "M", "𐳮"), + (0x10CAF, "M", "𐳯"), + (0x10CB0, "M", "𐳰"), + (0x10CB1, "M", "𐳱"), + (0x10CB2, "M", "𐳲"), + (0x10CB3, "X"), + (0x10CC0, "V"), + (0x10CF3, "X"), + (0x10CFA, "V"), + (0x10D28, "X"), + (0x10D30, "V"), + (0x10D3A, "X"), + (0x10E60, "V"), + (0x10E7F, "X"), + (0x10E80, "V"), + (0x10EAA, "X"), + (0x10EAB, "V"), + (0x10EAE, "X"), + (0x10EB0, "V"), + (0x10EB2, "X"), + (0x10EFD, "V"), + (0x10F28, "X"), + (0x10F30, "V"), + (0x10F5A, "X"), + (0x10F70, "V"), + (0x10F8A, "X"), + (0x10FB0, "V"), + (0x10FCC, "X"), + (0x10FE0, "V"), + (0x10FF7, "X"), + (0x11000, "V"), + (0x1104E, "X"), + (0x11052, "V"), + (0x11076, "X"), + (0x1107F, "V"), + (0x110BD, "X"), + (0x110BE, "V"), + (0x110C3, "X"), + (0x110D0, "V"), + (0x110E9, "X"), + (0x110F0, "V"), + ] + + +def _seg_57() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x110FA, "X"), + (0x11100, "V"), + (0x11135, "X"), + (0x11136, "V"), + (0x11148, "X"), + (0x11150, "V"), + (0x11177, "X"), + (0x11180, "V"), + (0x111E0, "X"), + (0x111E1, "V"), + (0x111F5, "X"), + (0x11200, "V"), + (0x11212, "X"), + (0x11213, "V"), + (0x11242, "X"), + (0x11280, "V"), + (0x11287, "X"), + (0x11288, "V"), + (0x11289, "X"), + (0x1128A, "V"), + (0x1128E, "X"), + (0x1128F, "V"), + (0x1129E, "X"), + (0x1129F, "V"), + (0x112AA, "X"), + (0x112B0, "V"), + (0x112EB, "X"), + (0x112F0, "V"), + (0x112FA, "X"), + (0x11300, "V"), + (0x11304, "X"), + (0x11305, "V"), + (0x1130D, "X"), + (0x1130F, "V"), + (0x11311, "X"), + (0x11313, "V"), + (0x11329, "X"), + (0x1132A, "V"), + (0x11331, "X"), + (0x11332, "V"), + (0x11334, "X"), + (0x11335, "V"), + (0x1133A, "X"), + (0x1133B, "V"), + (0x11345, "X"), + (0x11347, "V"), + (0x11349, "X"), + (0x1134B, "V"), + (0x1134E, "X"), + (0x11350, "V"), + (0x11351, "X"), + (0x11357, "V"), + (0x11358, "X"), + (0x1135D, "V"), + (0x11364, "X"), + (0x11366, "V"), + (0x1136D, "X"), + (0x11370, "V"), + (0x11375, "X"), + (0x11400, "V"), + (0x1145C, "X"), + (0x1145D, "V"), + (0x11462, "X"), + (0x11480, "V"), + (0x114C8, "X"), + (0x114D0, "V"), + (0x114DA, "X"), + (0x11580, "V"), + (0x115B6, "X"), + (0x115B8, "V"), + (0x115DE, "X"), + (0x11600, "V"), + (0x11645, "X"), + (0x11650, "V"), + (0x1165A, "X"), + (0x11660, "V"), + (0x1166D, "X"), + (0x11680, "V"), + (0x116BA, "X"), + (0x116C0, "V"), + (0x116CA, "X"), + (0x11700, "V"), + (0x1171B, "X"), + (0x1171D, "V"), + (0x1172C, "X"), + (0x11730, "V"), + (0x11747, "X"), + (0x11800, "V"), + (0x1183C, "X"), + (0x118A0, "M", "𑣀"), + (0x118A1, "M", "𑣁"), + (0x118A2, "M", "𑣂"), + (0x118A3, "M", "𑣃"), + (0x118A4, "M", "𑣄"), + (0x118A5, "M", "𑣅"), + (0x118A6, "M", "𑣆"), + (0x118A7, "M", "𑣇"), + (0x118A8, "M", "𑣈"), + (0x118A9, "M", "𑣉"), + (0x118AA, "M", "𑣊"), + ] + + +def _seg_58() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x118AB, "M", "𑣋"), + (0x118AC, "M", "𑣌"), + (0x118AD, "M", "𑣍"), + (0x118AE, "M", "𑣎"), + (0x118AF, "M", "𑣏"), + (0x118B0, "M", "𑣐"), + (0x118B1, "M", "𑣑"), + (0x118B2, "M", "𑣒"), + (0x118B3, "M", "𑣓"), + (0x118B4, "M", "𑣔"), + (0x118B5, "M", "𑣕"), + (0x118B6, "M", "𑣖"), + (0x118B7, "M", "𑣗"), + (0x118B8, "M", "𑣘"), + (0x118B9, "M", "𑣙"), + (0x118BA, "M", "𑣚"), + (0x118BB, "M", "𑣛"), + (0x118BC, "M", "𑣜"), + (0x118BD, "M", "𑣝"), + (0x118BE, "M", "𑣞"), + (0x118BF, "M", "𑣟"), + (0x118C0, "V"), + (0x118F3, "X"), + (0x118FF, "V"), + (0x11907, "X"), + (0x11909, "V"), + (0x1190A, "X"), + (0x1190C, "V"), + (0x11914, "X"), + (0x11915, "V"), + (0x11917, "X"), + (0x11918, "V"), + (0x11936, "X"), + (0x11937, "V"), + (0x11939, "X"), + (0x1193B, "V"), + (0x11947, "X"), + (0x11950, "V"), + (0x1195A, "X"), + (0x119A0, "V"), + (0x119A8, "X"), + (0x119AA, "V"), + (0x119D8, "X"), + (0x119DA, "V"), + (0x119E5, "X"), + (0x11A00, "V"), + (0x11A48, "X"), + (0x11A50, "V"), + (0x11AA3, "X"), + (0x11AB0, "V"), + (0x11AF9, "X"), + (0x11B00, "V"), + (0x11B0A, "X"), + (0x11C00, "V"), + (0x11C09, "X"), + (0x11C0A, "V"), + (0x11C37, "X"), + (0x11C38, "V"), + (0x11C46, "X"), + (0x11C50, "V"), + (0x11C6D, "X"), + (0x11C70, "V"), + (0x11C90, "X"), + (0x11C92, "V"), + (0x11CA8, "X"), + (0x11CA9, "V"), + (0x11CB7, "X"), + (0x11D00, "V"), + (0x11D07, "X"), + (0x11D08, "V"), + (0x11D0A, "X"), + (0x11D0B, "V"), + (0x11D37, "X"), + (0x11D3A, "V"), + (0x11D3B, "X"), + (0x11D3C, "V"), + (0x11D3E, "X"), + (0x11D3F, "V"), + (0x11D48, "X"), + (0x11D50, "V"), + (0x11D5A, "X"), + (0x11D60, "V"), + (0x11D66, "X"), + (0x11D67, "V"), + (0x11D69, "X"), + (0x11D6A, "V"), + (0x11D8F, "X"), + (0x11D90, "V"), + (0x11D92, "X"), + (0x11D93, "V"), + (0x11D99, "X"), + (0x11DA0, "V"), + (0x11DAA, "X"), + (0x11EE0, "V"), + (0x11EF9, "X"), + (0x11F00, "V"), + (0x11F11, "X"), + (0x11F12, "V"), + (0x11F3B, "X"), + (0x11F3E, "V"), + ] + + +def _seg_59() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x11F5A, "X"), + (0x11FB0, "V"), + (0x11FB1, "X"), + (0x11FC0, "V"), + (0x11FF2, "X"), + (0x11FFF, "V"), + (0x1239A, "X"), + (0x12400, "V"), + (0x1246F, "X"), + (0x12470, "V"), + (0x12475, "X"), + (0x12480, "V"), + (0x12544, "X"), + (0x12F90, "V"), + (0x12FF3, "X"), + (0x13000, "V"), + (0x13430, "X"), + (0x13440, "V"), + (0x13456, "X"), + (0x14400, "V"), + (0x14647, "X"), + (0x16800, "V"), + (0x16A39, "X"), + (0x16A40, "V"), + (0x16A5F, "X"), + (0x16A60, "V"), + (0x16A6A, "X"), + (0x16A6E, "V"), + (0x16ABF, "X"), + (0x16AC0, "V"), + (0x16ACA, "X"), + (0x16AD0, "V"), + (0x16AEE, "X"), + (0x16AF0, "V"), + (0x16AF6, "X"), + (0x16B00, "V"), + (0x16B46, "X"), + (0x16B50, "V"), + (0x16B5A, "X"), + (0x16B5B, "V"), + (0x16B62, "X"), + (0x16B63, "V"), + (0x16B78, "X"), + (0x16B7D, "V"), + (0x16B90, "X"), + (0x16E40, "M", "𖹠"), + (0x16E41, "M", "𖹡"), + (0x16E42, "M", "𖹢"), + (0x16E43, "M", "𖹣"), + (0x16E44, "M", "𖹤"), + (0x16E45, "M", "𖹥"), + (0x16E46, "M", "𖹦"), + (0x16E47, "M", "𖹧"), + (0x16E48, "M", "𖹨"), + (0x16E49, "M", "𖹩"), + (0x16E4A, "M", "𖹪"), + (0x16E4B, "M", "𖹫"), + (0x16E4C, "M", "𖹬"), + (0x16E4D, "M", "𖹭"), + (0x16E4E, "M", "𖹮"), + (0x16E4F, "M", "𖹯"), + (0x16E50, "M", "𖹰"), + (0x16E51, "M", "𖹱"), + (0x16E52, "M", "𖹲"), + (0x16E53, "M", "𖹳"), + (0x16E54, "M", "𖹴"), + (0x16E55, "M", "𖹵"), + (0x16E56, "M", "𖹶"), + (0x16E57, "M", "𖹷"), + (0x16E58, "M", "𖹸"), + (0x16E59, "M", "𖹹"), + (0x16E5A, "M", "𖹺"), + (0x16E5B, "M", "𖹻"), + (0x16E5C, "M", "𖹼"), + (0x16E5D, "M", "𖹽"), + (0x16E5E, "M", "𖹾"), + (0x16E5F, "M", "𖹿"), + (0x16E60, "V"), + (0x16E9B, "X"), + (0x16F00, "V"), + (0x16F4B, "X"), + (0x16F4F, "V"), + (0x16F88, "X"), + (0x16F8F, "V"), + (0x16FA0, "X"), + (0x16FE0, "V"), + (0x16FE5, "X"), + (0x16FF0, "V"), + (0x16FF2, "X"), + (0x17000, "V"), + (0x187F8, "X"), + (0x18800, "V"), + (0x18CD6, "X"), + (0x18D00, "V"), + (0x18D09, "X"), + (0x1AFF0, "V"), + (0x1AFF4, "X"), + (0x1AFF5, "V"), + (0x1AFFC, "X"), + (0x1AFFD, "V"), + ] + + +def _seg_60() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1AFFF, "X"), + (0x1B000, "V"), + (0x1B123, "X"), + (0x1B132, "V"), + (0x1B133, "X"), + (0x1B150, "V"), + (0x1B153, "X"), + (0x1B155, "V"), + (0x1B156, "X"), + (0x1B164, "V"), + (0x1B168, "X"), + (0x1B170, "V"), + (0x1B2FC, "X"), + (0x1BC00, "V"), + (0x1BC6B, "X"), + (0x1BC70, "V"), + (0x1BC7D, "X"), + (0x1BC80, "V"), + (0x1BC89, "X"), + (0x1BC90, "V"), + (0x1BC9A, "X"), + (0x1BC9C, "V"), + (0x1BCA0, "I"), + (0x1BCA4, "X"), + (0x1CF00, "V"), + (0x1CF2E, "X"), + (0x1CF30, "V"), + (0x1CF47, "X"), + (0x1CF50, "V"), + (0x1CFC4, "X"), + (0x1D000, "V"), + (0x1D0F6, "X"), + (0x1D100, "V"), + (0x1D127, "X"), + (0x1D129, "V"), + (0x1D15E, "M", "𝅗𝅥"), + (0x1D15F, "M", "𝅘𝅥"), + (0x1D160, "M", "𝅘𝅥𝅮"), + (0x1D161, "M", "𝅘𝅥𝅯"), + (0x1D162, "M", "𝅘𝅥𝅰"), + (0x1D163, "M", "𝅘𝅥𝅱"), + (0x1D164, "M", "𝅘𝅥𝅲"), + (0x1D165, "V"), + (0x1D173, "X"), + (0x1D17B, "V"), + (0x1D1BB, "M", "𝆹𝅥"), + (0x1D1BC, "M", "𝆺𝅥"), + (0x1D1BD, "M", "𝆹𝅥𝅮"), + (0x1D1BE, "M", "𝆺𝅥𝅮"), + (0x1D1BF, "M", "𝆹𝅥𝅯"), + (0x1D1C0, "M", "𝆺𝅥𝅯"), + (0x1D1C1, "V"), + (0x1D1EB, "X"), + (0x1D200, "V"), + (0x1D246, "X"), + (0x1D2C0, "V"), + (0x1D2D4, "X"), + (0x1D2E0, "V"), + (0x1D2F4, "X"), + (0x1D300, "V"), + (0x1D357, "X"), + (0x1D360, "V"), + (0x1D379, "X"), + (0x1D400, "M", "a"), + (0x1D401, "M", "b"), + (0x1D402, "M", "c"), + (0x1D403, "M", "d"), + (0x1D404, "M", "e"), + (0x1D405, "M", "f"), + (0x1D406, "M", "g"), + (0x1D407, "M", "h"), + (0x1D408, "M", "i"), + (0x1D409, "M", "j"), + (0x1D40A, "M", "k"), + (0x1D40B, "M", "l"), + (0x1D40C, "M", "m"), + (0x1D40D, "M", "n"), + (0x1D40E, "M", "o"), + (0x1D40F, "M", "p"), + (0x1D410, "M", "q"), + (0x1D411, "M", "r"), + (0x1D412, "M", "s"), + (0x1D413, "M", "t"), + (0x1D414, "M", "u"), + (0x1D415, "M", "v"), + (0x1D416, "M", "w"), + (0x1D417, "M", "x"), + (0x1D418, "M", "y"), + (0x1D419, "M", "z"), + (0x1D41A, "M", "a"), + (0x1D41B, "M", "b"), + (0x1D41C, "M", "c"), + (0x1D41D, "M", "d"), + (0x1D41E, "M", "e"), + (0x1D41F, "M", "f"), + (0x1D420, "M", "g"), + (0x1D421, "M", "h"), + (0x1D422, "M", "i"), + (0x1D423, "M", "j"), + (0x1D424, "M", "k"), + ] + + +def _seg_61() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1D425, "M", "l"), + (0x1D426, "M", "m"), + (0x1D427, "M", "n"), + (0x1D428, "M", "o"), + (0x1D429, "M", "p"), + (0x1D42A, "M", "q"), + (0x1D42B, "M", "r"), + (0x1D42C, "M", "s"), + (0x1D42D, "M", "t"), + (0x1D42E, "M", "u"), + (0x1D42F, "M", "v"), + (0x1D430, "M", "w"), + (0x1D431, "M", "x"), + (0x1D432, "M", "y"), + (0x1D433, "M", "z"), + (0x1D434, "M", "a"), + (0x1D435, "M", "b"), + (0x1D436, "M", "c"), + (0x1D437, "M", "d"), + (0x1D438, "M", "e"), + (0x1D439, "M", "f"), + (0x1D43A, "M", "g"), + (0x1D43B, "M", "h"), + (0x1D43C, "M", "i"), + (0x1D43D, "M", "j"), + (0x1D43E, "M", "k"), + (0x1D43F, "M", "l"), + (0x1D440, "M", "m"), + (0x1D441, "M", "n"), + (0x1D442, "M", "o"), + (0x1D443, "M", "p"), + (0x1D444, "M", "q"), + (0x1D445, "M", "r"), + (0x1D446, "M", "s"), + (0x1D447, "M", "t"), + (0x1D448, "M", "u"), + (0x1D449, "M", "v"), + (0x1D44A, "M", "w"), + (0x1D44B, "M", "x"), + (0x1D44C, "M", "y"), + (0x1D44D, "M", "z"), + (0x1D44E, "M", "a"), + (0x1D44F, "M", "b"), + (0x1D450, "M", "c"), + (0x1D451, "M", "d"), + (0x1D452, "M", "e"), + (0x1D453, "M", "f"), + (0x1D454, "M", "g"), + (0x1D455, "X"), + (0x1D456, "M", "i"), + (0x1D457, "M", "j"), + (0x1D458, "M", "k"), + (0x1D459, "M", "l"), + (0x1D45A, "M", "m"), + (0x1D45B, "M", "n"), + (0x1D45C, "M", "o"), + (0x1D45D, "M", "p"), + (0x1D45E, "M", "q"), + (0x1D45F, "M", "r"), + (0x1D460, "M", "s"), + (0x1D461, "M", "t"), + (0x1D462, "M", "u"), + (0x1D463, "M", "v"), + (0x1D464, "M", "w"), + (0x1D465, "M", "x"), + (0x1D466, "M", "y"), + (0x1D467, "M", "z"), + (0x1D468, "M", "a"), + (0x1D469, "M", "b"), + (0x1D46A, "M", "c"), + (0x1D46B, "M", "d"), + (0x1D46C, "M", "e"), + (0x1D46D, "M", "f"), + (0x1D46E, "M", "g"), + (0x1D46F, "M", "h"), + (0x1D470, "M", "i"), + (0x1D471, "M", "j"), + (0x1D472, "M", "k"), + (0x1D473, "M", "l"), + (0x1D474, "M", "m"), + (0x1D475, "M", "n"), + (0x1D476, "M", "o"), + (0x1D477, "M", "p"), + (0x1D478, "M", "q"), + (0x1D479, "M", "r"), + (0x1D47A, "M", "s"), + (0x1D47B, "M", "t"), + (0x1D47C, "M", "u"), + (0x1D47D, "M", "v"), + (0x1D47E, "M", "w"), + (0x1D47F, "M", "x"), + (0x1D480, "M", "y"), + (0x1D481, "M", "z"), + (0x1D482, "M", "a"), + (0x1D483, "M", "b"), + (0x1D484, "M", "c"), + (0x1D485, "M", "d"), + (0x1D486, "M", "e"), + (0x1D487, "M", "f"), + (0x1D488, "M", "g"), + ] + + +def _seg_62() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1D489, "M", "h"), + (0x1D48A, "M", "i"), + (0x1D48B, "M", "j"), + (0x1D48C, "M", "k"), + (0x1D48D, "M", "l"), + (0x1D48E, "M", "m"), + (0x1D48F, "M", "n"), + (0x1D490, "M", "o"), + (0x1D491, "M", "p"), + (0x1D492, "M", "q"), + (0x1D493, "M", "r"), + (0x1D494, "M", "s"), + (0x1D495, "M", "t"), + (0x1D496, "M", "u"), + (0x1D497, "M", "v"), + (0x1D498, "M", "w"), + (0x1D499, "M", "x"), + (0x1D49A, "M", "y"), + (0x1D49B, "M", "z"), + (0x1D49C, "M", "a"), + (0x1D49D, "X"), + (0x1D49E, "M", "c"), + (0x1D49F, "M", "d"), + (0x1D4A0, "X"), + (0x1D4A2, "M", "g"), + (0x1D4A3, "X"), + (0x1D4A5, "M", "j"), + (0x1D4A6, "M", "k"), + (0x1D4A7, "X"), + (0x1D4A9, "M", "n"), + (0x1D4AA, "M", "o"), + (0x1D4AB, "M", "p"), + (0x1D4AC, "M", "q"), + (0x1D4AD, "X"), + (0x1D4AE, "M", "s"), + (0x1D4AF, "M", "t"), + (0x1D4B0, "M", "u"), + (0x1D4B1, "M", "v"), + (0x1D4B2, "M", "w"), + (0x1D4B3, "M", "x"), + (0x1D4B4, "M", "y"), + (0x1D4B5, "M", "z"), + (0x1D4B6, "M", "a"), + (0x1D4B7, "M", "b"), + (0x1D4B8, "M", "c"), + (0x1D4B9, "M", "d"), + (0x1D4BA, "X"), + (0x1D4BB, "M", "f"), + (0x1D4BC, "X"), + (0x1D4BD, "M", "h"), + (0x1D4BE, "M", "i"), + (0x1D4BF, "M", "j"), + (0x1D4C0, "M", "k"), + (0x1D4C1, "M", "l"), + (0x1D4C2, "M", "m"), + (0x1D4C3, "M", "n"), + (0x1D4C4, "X"), + (0x1D4C5, "M", "p"), + (0x1D4C6, "M", "q"), + (0x1D4C7, "M", "r"), + (0x1D4C8, "M", "s"), + (0x1D4C9, "M", "t"), + (0x1D4CA, "M", "u"), + (0x1D4CB, "M", "v"), + (0x1D4CC, "M", "w"), + (0x1D4CD, "M", "x"), + (0x1D4CE, "M", "y"), + (0x1D4CF, "M", "z"), + (0x1D4D0, "M", "a"), + (0x1D4D1, "M", "b"), + (0x1D4D2, "M", "c"), + (0x1D4D3, "M", "d"), + (0x1D4D4, "M", "e"), + (0x1D4D5, "M", "f"), + (0x1D4D6, "M", "g"), + (0x1D4D7, "M", "h"), + (0x1D4D8, "M", "i"), + (0x1D4D9, "M", "j"), + (0x1D4DA, "M", "k"), + (0x1D4DB, "M", "l"), + (0x1D4DC, "M", "m"), + (0x1D4DD, "M", "n"), + (0x1D4DE, "M", "o"), + (0x1D4DF, "M", "p"), + (0x1D4E0, "M", "q"), + (0x1D4E1, "M", "r"), + (0x1D4E2, "M", "s"), + (0x1D4E3, "M", "t"), + (0x1D4E4, "M", "u"), + (0x1D4E5, "M", "v"), + (0x1D4E6, "M", "w"), + (0x1D4E7, "M", "x"), + (0x1D4E8, "M", "y"), + (0x1D4E9, "M", "z"), + (0x1D4EA, "M", "a"), + (0x1D4EB, "M", "b"), + (0x1D4EC, "M", "c"), + (0x1D4ED, "M", "d"), + (0x1D4EE, "M", "e"), + (0x1D4EF, "M", "f"), + ] + + +def _seg_63() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1D4F0, "M", "g"), + (0x1D4F1, "M", "h"), + (0x1D4F2, "M", "i"), + (0x1D4F3, "M", "j"), + (0x1D4F4, "M", "k"), + (0x1D4F5, "M", "l"), + (0x1D4F6, "M", "m"), + (0x1D4F7, "M", "n"), + (0x1D4F8, "M", "o"), + (0x1D4F9, "M", "p"), + (0x1D4FA, "M", "q"), + (0x1D4FB, "M", "r"), + (0x1D4FC, "M", "s"), + (0x1D4FD, "M", "t"), + (0x1D4FE, "M", "u"), + (0x1D4FF, "M", "v"), + (0x1D500, "M", "w"), + (0x1D501, "M", "x"), + (0x1D502, "M", "y"), + (0x1D503, "M", "z"), + (0x1D504, "M", "a"), + (0x1D505, "M", "b"), + (0x1D506, "X"), + (0x1D507, "M", "d"), + (0x1D508, "M", "e"), + (0x1D509, "M", "f"), + (0x1D50A, "M", "g"), + (0x1D50B, "X"), + (0x1D50D, "M", "j"), + (0x1D50E, "M", "k"), + (0x1D50F, "M", "l"), + (0x1D510, "M", "m"), + (0x1D511, "M", "n"), + (0x1D512, "M", "o"), + (0x1D513, "M", "p"), + (0x1D514, "M", "q"), + (0x1D515, "X"), + (0x1D516, "M", "s"), + (0x1D517, "M", "t"), + (0x1D518, "M", "u"), + (0x1D519, "M", "v"), + (0x1D51A, "M", "w"), + (0x1D51B, "M", "x"), + (0x1D51C, "M", "y"), + (0x1D51D, "X"), + (0x1D51E, "M", "a"), + (0x1D51F, "M", "b"), + (0x1D520, "M", "c"), + (0x1D521, "M", "d"), + (0x1D522, "M", "e"), + (0x1D523, "M", "f"), + (0x1D524, "M", "g"), + (0x1D525, "M", "h"), + (0x1D526, "M", "i"), + (0x1D527, "M", "j"), + (0x1D528, "M", "k"), + (0x1D529, "M", "l"), + (0x1D52A, "M", "m"), + (0x1D52B, "M", "n"), + (0x1D52C, "M", "o"), + (0x1D52D, "M", "p"), + (0x1D52E, "M", "q"), + (0x1D52F, "M", "r"), + (0x1D530, "M", "s"), + (0x1D531, "M", "t"), + (0x1D532, "M", "u"), + (0x1D533, "M", "v"), + (0x1D534, "M", "w"), + (0x1D535, "M", "x"), + (0x1D536, "M", "y"), + (0x1D537, "M", "z"), + (0x1D538, "M", "a"), + (0x1D539, "M", "b"), + (0x1D53A, "X"), + (0x1D53B, "M", "d"), + (0x1D53C, "M", "e"), + (0x1D53D, "M", "f"), + (0x1D53E, "M", "g"), + (0x1D53F, "X"), + (0x1D540, "M", "i"), + (0x1D541, "M", "j"), + (0x1D542, "M", "k"), + (0x1D543, "M", "l"), + (0x1D544, "M", "m"), + (0x1D545, "X"), + (0x1D546, "M", "o"), + (0x1D547, "X"), + (0x1D54A, "M", "s"), + (0x1D54B, "M", "t"), + (0x1D54C, "M", "u"), + (0x1D54D, "M", "v"), + (0x1D54E, "M", "w"), + (0x1D54F, "M", "x"), + (0x1D550, "M", "y"), + (0x1D551, "X"), + (0x1D552, "M", "a"), + (0x1D553, "M", "b"), + (0x1D554, "M", "c"), + (0x1D555, "M", "d"), + (0x1D556, "M", "e"), + ] + + +def _seg_64() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1D557, "M", "f"), + (0x1D558, "M", "g"), + (0x1D559, "M", "h"), + (0x1D55A, "M", "i"), + (0x1D55B, "M", "j"), + (0x1D55C, "M", "k"), + (0x1D55D, "M", "l"), + (0x1D55E, "M", "m"), + (0x1D55F, "M", "n"), + (0x1D560, "M", "o"), + (0x1D561, "M", "p"), + (0x1D562, "M", "q"), + (0x1D563, "M", "r"), + (0x1D564, "M", "s"), + (0x1D565, "M", "t"), + (0x1D566, "M", "u"), + (0x1D567, "M", "v"), + (0x1D568, "M", "w"), + (0x1D569, "M", "x"), + (0x1D56A, "M", "y"), + (0x1D56B, "M", "z"), + (0x1D56C, "M", "a"), + (0x1D56D, "M", "b"), + (0x1D56E, "M", "c"), + (0x1D56F, "M", "d"), + (0x1D570, "M", "e"), + (0x1D571, "M", "f"), + (0x1D572, "M", "g"), + (0x1D573, "M", "h"), + (0x1D574, "M", "i"), + (0x1D575, "M", "j"), + (0x1D576, "M", "k"), + (0x1D577, "M", "l"), + (0x1D578, "M", "m"), + (0x1D579, "M", "n"), + (0x1D57A, "M", "o"), + (0x1D57B, "M", "p"), + (0x1D57C, "M", "q"), + (0x1D57D, "M", "r"), + (0x1D57E, "M", "s"), + (0x1D57F, "M", "t"), + (0x1D580, "M", "u"), + (0x1D581, "M", "v"), + (0x1D582, "M", "w"), + (0x1D583, "M", "x"), + (0x1D584, "M", "y"), + (0x1D585, "M", "z"), + (0x1D586, "M", "a"), + (0x1D587, "M", "b"), + (0x1D588, "M", "c"), + (0x1D589, "M", "d"), + (0x1D58A, "M", "e"), + (0x1D58B, "M", "f"), + (0x1D58C, "M", "g"), + (0x1D58D, "M", "h"), + (0x1D58E, "M", "i"), + (0x1D58F, "M", "j"), + (0x1D590, "M", "k"), + (0x1D591, "M", "l"), + (0x1D592, "M", "m"), + (0x1D593, "M", "n"), + (0x1D594, "M", "o"), + (0x1D595, "M", "p"), + (0x1D596, "M", "q"), + (0x1D597, "M", "r"), + (0x1D598, "M", "s"), + (0x1D599, "M", "t"), + (0x1D59A, "M", "u"), + (0x1D59B, "M", "v"), + (0x1D59C, "M", "w"), + (0x1D59D, "M", "x"), + (0x1D59E, "M", "y"), + (0x1D59F, "M", "z"), + (0x1D5A0, "M", "a"), + (0x1D5A1, "M", "b"), + (0x1D5A2, "M", "c"), + (0x1D5A3, "M", "d"), + (0x1D5A4, "M", "e"), + (0x1D5A5, "M", "f"), + (0x1D5A6, "M", "g"), + (0x1D5A7, "M", "h"), + (0x1D5A8, "M", "i"), + (0x1D5A9, "M", "j"), + (0x1D5AA, "M", "k"), + (0x1D5AB, "M", "l"), + (0x1D5AC, "M", "m"), + (0x1D5AD, "M", "n"), + (0x1D5AE, "M", "o"), + (0x1D5AF, "M", "p"), + (0x1D5B0, "M", "q"), + (0x1D5B1, "M", "r"), + (0x1D5B2, "M", "s"), + (0x1D5B3, "M", "t"), + (0x1D5B4, "M", "u"), + (0x1D5B5, "M", "v"), + (0x1D5B6, "M", "w"), + (0x1D5B7, "M", "x"), + (0x1D5B8, "M", "y"), + (0x1D5B9, "M", "z"), + (0x1D5BA, "M", "a"), + ] + + +def _seg_65() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1D5BB, "M", "b"), + (0x1D5BC, "M", "c"), + (0x1D5BD, "M", "d"), + (0x1D5BE, "M", "e"), + (0x1D5BF, "M", "f"), + (0x1D5C0, "M", "g"), + (0x1D5C1, "M", "h"), + (0x1D5C2, "M", "i"), + (0x1D5C3, "M", "j"), + (0x1D5C4, "M", "k"), + (0x1D5C5, "M", "l"), + (0x1D5C6, "M", "m"), + (0x1D5C7, "M", "n"), + (0x1D5C8, "M", "o"), + (0x1D5C9, "M", "p"), + (0x1D5CA, "M", "q"), + (0x1D5CB, "M", "r"), + (0x1D5CC, "M", "s"), + (0x1D5CD, "M", "t"), + (0x1D5CE, "M", "u"), + (0x1D5CF, "M", "v"), + (0x1D5D0, "M", "w"), + (0x1D5D1, "M", "x"), + (0x1D5D2, "M", "y"), + (0x1D5D3, "M", "z"), + (0x1D5D4, "M", "a"), + (0x1D5D5, "M", "b"), + (0x1D5D6, "M", "c"), + (0x1D5D7, "M", "d"), + (0x1D5D8, "M", "e"), + (0x1D5D9, "M", "f"), + (0x1D5DA, "M", "g"), + (0x1D5DB, "M", "h"), + (0x1D5DC, "M", "i"), + (0x1D5DD, "M", "j"), + (0x1D5DE, "M", "k"), + (0x1D5DF, "M", "l"), + (0x1D5E0, "M", "m"), + (0x1D5E1, "M", "n"), + (0x1D5E2, "M", "o"), + (0x1D5E3, "M", "p"), + (0x1D5E4, "M", "q"), + (0x1D5E5, "M", "r"), + (0x1D5E6, "M", "s"), + (0x1D5E7, "M", "t"), + (0x1D5E8, "M", "u"), + (0x1D5E9, "M", "v"), + (0x1D5EA, "M", "w"), + (0x1D5EB, "M", "x"), + (0x1D5EC, "M", "y"), + (0x1D5ED, "M", "z"), + (0x1D5EE, "M", "a"), + (0x1D5EF, "M", "b"), + (0x1D5F0, "M", "c"), + (0x1D5F1, "M", "d"), + (0x1D5F2, "M", "e"), + (0x1D5F3, "M", "f"), + (0x1D5F4, "M", "g"), + (0x1D5F5, "M", "h"), + (0x1D5F6, "M", "i"), + (0x1D5F7, "M", "j"), + (0x1D5F8, "M", "k"), + (0x1D5F9, "M", "l"), + (0x1D5FA, "M", "m"), + (0x1D5FB, "M", "n"), + (0x1D5FC, "M", "o"), + (0x1D5FD, "M", "p"), + (0x1D5FE, "M", "q"), + (0x1D5FF, "M", "r"), + (0x1D600, "M", "s"), + (0x1D601, "M", "t"), + (0x1D602, "M", "u"), + (0x1D603, "M", "v"), + (0x1D604, "M", "w"), + (0x1D605, "M", "x"), + (0x1D606, "M", "y"), + (0x1D607, "M", "z"), + (0x1D608, "M", "a"), + (0x1D609, "M", "b"), + (0x1D60A, "M", "c"), + (0x1D60B, "M", "d"), + (0x1D60C, "M", "e"), + (0x1D60D, "M", "f"), + (0x1D60E, "M", "g"), + (0x1D60F, "M", "h"), + (0x1D610, "M", "i"), + (0x1D611, "M", "j"), + (0x1D612, "M", "k"), + (0x1D613, "M", "l"), + (0x1D614, "M", "m"), + (0x1D615, "M", "n"), + (0x1D616, "M", "o"), + (0x1D617, "M", "p"), + (0x1D618, "M", "q"), + (0x1D619, "M", "r"), + (0x1D61A, "M", "s"), + (0x1D61B, "M", "t"), + (0x1D61C, "M", "u"), + (0x1D61D, "M", "v"), + (0x1D61E, "M", "w"), + ] + + +def _seg_66() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1D61F, "M", "x"), + (0x1D620, "M", "y"), + (0x1D621, "M", "z"), + (0x1D622, "M", "a"), + (0x1D623, "M", "b"), + (0x1D624, "M", "c"), + (0x1D625, "M", "d"), + (0x1D626, "M", "e"), + (0x1D627, "M", "f"), + (0x1D628, "M", "g"), + (0x1D629, "M", "h"), + (0x1D62A, "M", "i"), + (0x1D62B, "M", "j"), + (0x1D62C, "M", "k"), + (0x1D62D, "M", "l"), + (0x1D62E, "M", "m"), + (0x1D62F, "M", "n"), + (0x1D630, "M", "o"), + (0x1D631, "M", "p"), + (0x1D632, "M", "q"), + (0x1D633, "M", "r"), + (0x1D634, "M", "s"), + (0x1D635, "M", "t"), + (0x1D636, "M", "u"), + (0x1D637, "M", "v"), + (0x1D638, "M", "w"), + (0x1D639, "M", "x"), + (0x1D63A, "M", "y"), + (0x1D63B, "M", "z"), + (0x1D63C, "M", "a"), + (0x1D63D, "M", "b"), + (0x1D63E, "M", "c"), + (0x1D63F, "M", "d"), + (0x1D640, "M", "e"), + (0x1D641, "M", "f"), + (0x1D642, "M", "g"), + (0x1D643, "M", "h"), + (0x1D644, "M", "i"), + (0x1D645, "M", "j"), + (0x1D646, "M", "k"), + (0x1D647, "M", "l"), + (0x1D648, "M", "m"), + (0x1D649, "M", "n"), + (0x1D64A, "M", "o"), + (0x1D64B, "M", "p"), + (0x1D64C, "M", "q"), + (0x1D64D, "M", "r"), + (0x1D64E, "M", "s"), + (0x1D64F, "M", "t"), + (0x1D650, "M", "u"), + (0x1D651, "M", "v"), + (0x1D652, "M", "w"), + (0x1D653, "M", "x"), + (0x1D654, "M", "y"), + (0x1D655, "M", "z"), + (0x1D656, "M", "a"), + (0x1D657, "M", "b"), + (0x1D658, "M", "c"), + (0x1D659, "M", "d"), + (0x1D65A, "M", "e"), + (0x1D65B, "M", "f"), + (0x1D65C, "M", "g"), + (0x1D65D, "M", "h"), + (0x1D65E, "M", "i"), + (0x1D65F, "M", "j"), + (0x1D660, "M", "k"), + (0x1D661, "M", "l"), + (0x1D662, "M", "m"), + (0x1D663, "M", "n"), + (0x1D664, "M", "o"), + (0x1D665, "M", "p"), + (0x1D666, "M", "q"), + (0x1D667, "M", "r"), + (0x1D668, "M", "s"), + (0x1D669, "M", "t"), + (0x1D66A, "M", "u"), + (0x1D66B, "M", "v"), + (0x1D66C, "M", "w"), + (0x1D66D, "M", "x"), + (0x1D66E, "M", "y"), + (0x1D66F, "M", "z"), + (0x1D670, "M", "a"), + (0x1D671, "M", "b"), + (0x1D672, "M", "c"), + (0x1D673, "M", "d"), + (0x1D674, "M", "e"), + (0x1D675, "M", "f"), + (0x1D676, "M", "g"), + (0x1D677, "M", "h"), + (0x1D678, "M", "i"), + (0x1D679, "M", "j"), + (0x1D67A, "M", "k"), + (0x1D67B, "M", "l"), + (0x1D67C, "M", "m"), + (0x1D67D, "M", "n"), + (0x1D67E, "M", "o"), + (0x1D67F, "M", "p"), + (0x1D680, "M", "q"), + (0x1D681, "M", "r"), + (0x1D682, "M", "s"), + ] + + +def _seg_67() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1D683, "M", "t"), + (0x1D684, "M", "u"), + (0x1D685, "M", "v"), + (0x1D686, "M", "w"), + (0x1D687, "M", "x"), + (0x1D688, "M", "y"), + (0x1D689, "M", "z"), + (0x1D68A, "M", "a"), + (0x1D68B, "M", "b"), + (0x1D68C, "M", "c"), + (0x1D68D, "M", "d"), + (0x1D68E, "M", "e"), + (0x1D68F, "M", "f"), + (0x1D690, "M", "g"), + (0x1D691, "M", "h"), + (0x1D692, "M", "i"), + (0x1D693, "M", "j"), + (0x1D694, "M", "k"), + (0x1D695, "M", "l"), + (0x1D696, "M", "m"), + (0x1D697, "M", "n"), + (0x1D698, "M", "o"), + (0x1D699, "M", "p"), + (0x1D69A, "M", "q"), + (0x1D69B, "M", "r"), + (0x1D69C, "M", "s"), + (0x1D69D, "M", "t"), + (0x1D69E, "M", "u"), + (0x1D69F, "M", "v"), + (0x1D6A0, "M", "w"), + (0x1D6A1, "M", "x"), + (0x1D6A2, "M", "y"), + (0x1D6A3, "M", "z"), + (0x1D6A4, "M", "ı"), + (0x1D6A5, "M", "ȷ"), + (0x1D6A6, "X"), + (0x1D6A8, "M", "α"), + (0x1D6A9, "M", "β"), + (0x1D6AA, "M", "γ"), + (0x1D6AB, "M", "δ"), + (0x1D6AC, "M", "ε"), + (0x1D6AD, "M", "ζ"), + (0x1D6AE, "M", "η"), + (0x1D6AF, "M", "θ"), + (0x1D6B0, "M", "ι"), + (0x1D6B1, "M", "κ"), + (0x1D6B2, "M", "λ"), + (0x1D6B3, "M", "μ"), + (0x1D6B4, "M", "ν"), + (0x1D6B5, "M", "ξ"), + (0x1D6B6, "M", "ο"), + (0x1D6B7, "M", "π"), + (0x1D6B8, "M", "ρ"), + (0x1D6B9, "M", "θ"), + (0x1D6BA, "M", "σ"), + (0x1D6BB, "M", "τ"), + (0x1D6BC, "M", "υ"), + (0x1D6BD, "M", "φ"), + (0x1D6BE, "M", "χ"), + (0x1D6BF, "M", "ψ"), + (0x1D6C0, "M", "ω"), + (0x1D6C1, "M", "∇"), + (0x1D6C2, "M", "α"), + (0x1D6C3, "M", "β"), + (0x1D6C4, "M", "γ"), + (0x1D6C5, "M", "δ"), + (0x1D6C6, "M", "ε"), + (0x1D6C7, "M", "ζ"), + (0x1D6C8, "M", "η"), + (0x1D6C9, "M", "θ"), + (0x1D6CA, "M", "ι"), + (0x1D6CB, "M", "κ"), + (0x1D6CC, "M", "λ"), + (0x1D6CD, "M", "μ"), + (0x1D6CE, "M", "ν"), + (0x1D6CF, "M", "ξ"), + (0x1D6D0, "M", "ο"), + (0x1D6D1, "M", "π"), + (0x1D6D2, "M", "ρ"), + (0x1D6D3, "M", "σ"), + (0x1D6D5, "M", "τ"), + (0x1D6D6, "M", "υ"), + (0x1D6D7, "M", "φ"), + (0x1D6D8, "M", "χ"), + (0x1D6D9, "M", "ψ"), + (0x1D6DA, "M", "ω"), + (0x1D6DB, "M", "∂"), + (0x1D6DC, "M", "ε"), + (0x1D6DD, "M", "θ"), + (0x1D6DE, "M", "κ"), + (0x1D6DF, "M", "φ"), + (0x1D6E0, "M", "ρ"), + (0x1D6E1, "M", "π"), + (0x1D6E2, "M", "α"), + (0x1D6E3, "M", "β"), + (0x1D6E4, "M", "γ"), + (0x1D6E5, "M", "δ"), + (0x1D6E6, "M", "ε"), + (0x1D6E7, "M", "ζ"), + (0x1D6E8, "M", "η"), + ] + + +def _seg_68() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1D6E9, "M", "θ"), + (0x1D6EA, "M", "ι"), + (0x1D6EB, "M", "κ"), + (0x1D6EC, "M", "λ"), + (0x1D6ED, "M", "μ"), + (0x1D6EE, "M", "ν"), + (0x1D6EF, "M", "ξ"), + (0x1D6F0, "M", "ο"), + (0x1D6F1, "M", "π"), + (0x1D6F2, "M", "ρ"), + (0x1D6F3, "M", "θ"), + (0x1D6F4, "M", "σ"), + (0x1D6F5, "M", "τ"), + (0x1D6F6, "M", "υ"), + (0x1D6F7, "M", "φ"), + (0x1D6F8, "M", "χ"), + (0x1D6F9, "M", "ψ"), + (0x1D6FA, "M", "ω"), + (0x1D6FB, "M", "∇"), + (0x1D6FC, "M", "α"), + (0x1D6FD, "M", "β"), + (0x1D6FE, "M", "γ"), + (0x1D6FF, "M", "δ"), + (0x1D700, "M", "ε"), + (0x1D701, "M", "ζ"), + (0x1D702, "M", "η"), + (0x1D703, "M", "θ"), + (0x1D704, "M", "ι"), + (0x1D705, "M", "κ"), + (0x1D706, "M", "λ"), + (0x1D707, "M", "μ"), + (0x1D708, "M", "ν"), + (0x1D709, "M", "ξ"), + (0x1D70A, "M", "ο"), + (0x1D70B, "M", "π"), + (0x1D70C, "M", "ρ"), + (0x1D70D, "M", "σ"), + (0x1D70F, "M", "τ"), + (0x1D710, "M", "υ"), + (0x1D711, "M", "φ"), + (0x1D712, "M", "χ"), + (0x1D713, "M", "ψ"), + (0x1D714, "M", "ω"), + (0x1D715, "M", "∂"), + (0x1D716, "M", "ε"), + (0x1D717, "M", "θ"), + (0x1D718, "M", "κ"), + (0x1D719, "M", "φ"), + (0x1D71A, "M", "ρ"), + (0x1D71B, "M", "π"), + (0x1D71C, "M", "α"), + (0x1D71D, "M", "β"), + (0x1D71E, "M", "γ"), + (0x1D71F, "M", "δ"), + (0x1D720, "M", "ε"), + (0x1D721, "M", "ζ"), + (0x1D722, "M", "η"), + (0x1D723, "M", "θ"), + (0x1D724, "M", "ι"), + (0x1D725, "M", "κ"), + (0x1D726, "M", "λ"), + (0x1D727, "M", "μ"), + (0x1D728, "M", "ν"), + (0x1D729, "M", "ξ"), + (0x1D72A, "M", "ο"), + (0x1D72B, "M", "π"), + (0x1D72C, "M", "ρ"), + (0x1D72D, "M", "θ"), + (0x1D72E, "M", "σ"), + (0x1D72F, "M", "τ"), + (0x1D730, "M", "υ"), + (0x1D731, "M", "φ"), + (0x1D732, "M", "χ"), + (0x1D733, "M", "ψ"), + (0x1D734, "M", "ω"), + (0x1D735, "M", "∇"), + (0x1D736, "M", "α"), + (0x1D737, "M", "β"), + (0x1D738, "M", "γ"), + (0x1D739, "M", "δ"), + (0x1D73A, "M", "ε"), + (0x1D73B, "M", "ζ"), + (0x1D73C, "M", "η"), + (0x1D73D, "M", "θ"), + (0x1D73E, "M", "ι"), + (0x1D73F, "M", "κ"), + (0x1D740, "M", "λ"), + (0x1D741, "M", "μ"), + (0x1D742, "M", "ν"), + (0x1D743, "M", "ξ"), + (0x1D744, "M", "ο"), + (0x1D745, "M", "π"), + (0x1D746, "M", "ρ"), + (0x1D747, "M", "σ"), + (0x1D749, "M", "τ"), + (0x1D74A, "M", "υ"), + (0x1D74B, "M", "φ"), + (0x1D74C, "M", "χ"), + (0x1D74D, "M", "ψ"), + (0x1D74E, "M", "ω"), + ] + + +def _seg_69() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1D74F, "M", "∂"), + (0x1D750, "M", "ε"), + (0x1D751, "M", "θ"), + (0x1D752, "M", "κ"), + (0x1D753, "M", "φ"), + (0x1D754, "M", "ρ"), + (0x1D755, "M", "π"), + (0x1D756, "M", "α"), + (0x1D757, "M", "β"), + (0x1D758, "M", "γ"), + (0x1D759, "M", "δ"), + (0x1D75A, "M", "ε"), + (0x1D75B, "M", "ζ"), + (0x1D75C, "M", "η"), + (0x1D75D, "M", "θ"), + (0x1D75E, "M", "ι"), + (0x1D75F, "M", "κ"), + (0x1D760, "M", "λ"), + (0x1D761, "M", "μ"), + (0x1D762, "M", "ν"), + (0x1D763, "M", "ξ"), + (0x1D764, "M", "ο"), + (0x1D765, "M", "π"), + (0x1D766, "M", "ρ"), + (0x1D767, "M", "θ"), + (0x1D768, "M", "σ"), + (0x1D769, "M", "τ"), + (0x1D76A, "M", "υ"), + (0x1D76B, "M", "φ"), + (0x1D76C, "M", "χ"), + (0x1D76D, "M", "ψ"), + (0x1D76E, "M", "ω"), + (0x1D76F, "M", "∇"), + (0x1D770, "M", "α"), + (0x1D771, "M", "β"), + (0x1D772, "M", "γ"), + (0x1D773, "M", "δ"), + (0x1D774, "M", "ε"), + (0x1D775, "M", "ζ"), + (0x1D776, "M", "η"), + (0x1D777, "M", "θ"), + (0x1D778, "M", "ι"), + (0x1D779, "M", "κ"), + (0x1D77A, "M", "λ"), + (0x1D77B, "M", "μ"), + (0x1D77C, "M", "ν"), + (0x1D77D, "M", "ξ"), + (0x1D77E, "M", "ο"), + (0x1D77F, "M", "π"), + (0x1D780, "M", "ρ"), + (0x1D781, "M", "σ"), + (0x1D783, "M", "τ"), + (0x1D784, "M", "υ"), + (0x1D785, "M", "φ"), + (0x1D786, "M", "χ"), + (0x1D787, "M", "ψ"), + (0x1D788, "M", "ω"), + (0x1D789, "M", "∂"), + (0x1D78A, "M", "ε"), + (0x1D78B, "M", "θ"), + (0x1D78C, "M", "κ"), + (0x1D78D, "M", "φ"), + (0x1D78E, "M", "ρ"), + (0x1D78F, "M", "π"), + (0x1D790, "M", "α"), + (0x1D791, "M", "β"), + (0x1D792, "M", "γ"), + (0x1D793, "M", "δ"), + (0x1D794, "M", "ε"), + (0x1D795, "M", "ζ"), + (0x1D796, "M", "η"), + (0x1D797, "M", "θ"), + (0x1D798, "M", "ι"), + (0x1D799, "M", "κ"), + (0x1D79A, "M", "λ"), + (0x1D79B, "M", "μ"), + (0x1D79C, "M", "ν"), + (0x1D79D, "M", "ξ"), + (0x1D79E, "M", "ο"), + (0x1D79F, "M", "π"), + (0x1D7A0, "M", "ρ"), + (0x1D7A1, "M", "θ"), + (0x1D7A2, "M", "σ"), + (0x1D7A3, "M", "τ"), + (0x1D7A4, "M", "υ"), + (0x1D7A5, "M", "φ"), + (0x1D7A6, "M", "χ"), + (0x1D7A7, "M", "ψ"), + (0x1D7A8, "M", "ω"), + (0x1D7A9, "M", "∇"), + (0x1D7AA, "M", "α"), + (0x1D7AB, "M", "β"), + (0x1D7AC, "M", "γ"), + (0x1D7AD, "M", "δ"), + (0x1D7AE, "M", "ε"), + (0x1D7AF, "M", "ζ"), + (0x1D7B0, "M", "η"), + (0x1D7B1, "M", "θ"), + (0x1D7B2, "M", "ι"), + (0x1D7B3, "M", "κ"), + ] + + +def _seg_70() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1D7B4, "M", "λ"), + (0x1D7B5, "M", "μ"), + (0x1D7B6, "M", "ν"), + (0x1D7B7, "M", "ξ"), + (0x1D7B8, "M", "ο"), + (0x1D7B9, "M", "π"), + (0x1D7BA, "M", "ρ"), + (0x1D7BB, "M", "σ"), + (0x1D7BD, "M", "τ"), + (0x1D7BE, "M", "υ"), + (0x1D7BF, "M", "φ"), + (0x1D7C0, "M", "χ"), + (0x1D7C1, "M", "ψ"), + (0x1D7C2, "M", "ω"), + (0x1D7C3, "M", "∂"), + (0x1D7C4, "M", "ε"), + (0x1D7C5, "M", "θ"), + (0x1D7C6, "M", "κ"), + (0x1D7C7, "M", "φ"), + (0x1D7C8, "M", "ρ"), + (0x1D7C9, "M", "π"), + (0x1D7CA, "M", "ϝ"), + (0x1D7CC, "X"), + (0x1D7CE, "M", "0"), + (0x1D7CF, "M", "1"), + (0x1D7D0, "M", "2"), + (0x1D7D1, "M", "3"), + (0x1D7D2, "M", "4"), + (0x1D7D3, "M", "5"), + (0x1D7D4, "M", "6"), + (0x1D7D5, "M", "7"), + (0x1D7D6, "M", "8"), + (0x1D7D7, "M", "9"), + (0x1D7D8, "M", "0"), + (0x1D7D9, "M", "1"), + (0x1D7DA, "M", "2"), + (0x1D7DB, "M", "3"), + (0x1D7DC, "M", "4"), + (0x1D7DD, "M", "5"), + (0x1D7DE, "M", "6"), + (0x1D7DF, "M", "7"), + (0x1D7E0, "M", "8"), + (0x1D7E1, "M", "9"), + (0x1D7E2, "M", "0"), + (0x1D7E3, "M", "1"), + (0x1D7E4, "M", "2"), + (0x1D7E5, "M", "3"), + (0x1D7E6, "M", "4"), + (0x1D7E7, "M", "5"), + (0x1D7E8, "M", "6"), + (0x1D7E9, "M", "7"), + (0x1D7EA, "M", "8"), + (0x1D7EB, "M", "9"), + (0x1D7EC, "M", "0"), + (0x1D7ED, "M", "1"), + (0x1D7EE, "M", "2"), + (0x1D7EF, "M", "3"), + (0x1D7F0, "M", "4"), + (0x1D7F1, "M", "5"), + (0x1D7F2, "M", "6"), + (0x1D7F3, "M", "7"), + (0x1D7F4, "M", "8"), + (0x1D7F5, "M", "9"), + (0x1D7F6, "M", "0"), + (0x1D7F7, "M", "1"), + (0x1D7F8, "M", "2"), + (0x1D7F9, "M", "3"), + (0x1D7FA, "M", "4"), + (0x1D7FB, "M", "5"), + (0x1D7FC, "M", "6"), + (0x1D7FD, "M", "7"), + (0x1D7FE, "M", "8"), + (0x1D7FF, "M", "9"), + (0x1D800, "V"), + (0x1DA8C, "X"), + (0x1DA9B, "V"), + (0x1DAA0, "X"), + (0x1DAA1, "V"), + (0x1DAB0, "X"), + (0x1DF00, "V"), + (0x1DF1F, "X"), + (0x1DF25, "V"), + (0x1DF2B, "X"), + (0x1E000, "V"), + (0x1E007, "X"), + (0x1E008, "V"), + (0x1E019, "X"), + (0x1E01B, "V"), + (0x1E022, "X"), + (0x1E023, "V"), + (0x1E025, "X"), + (0x1E026, "V"), + (0x1E02B, "X"), + (0x1E030, "M", "а"), + (0x1E031, "M", "б"), + (0x1E032, "M", "в"), + (0x1E033, "M", "г"), + (0x1E034, "M", "д"), + (0x1E035, "M", "е"), + (0x1E036, "M", "ж"), + ] + + +def _seg_71() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1E037, "M", "з"), + (0x1E038, "M", "и"), + (0x1E039, "M", "к"), + (0x1E03A, "M", "л"), + (0x1E03B, "M", "м"), + (0x1E03C, "M", "о"), + (0x1E03D, "M", "п"), + (0x1E03E, "M", "р"), + (0x1E03F, "M", "с"), + (0x1E040, "M", "т"), + (0x1E041, "M", "у"), + (0x1E042, "M", "ф"), + (0x1E043, "M", "х"), + (0x1E044, "M", "ц"), + (0x1E045, "M", "ч"), + (0x1E046, "M", "ш"), + (0x1E047, "M", "ы"), + (0x1E048, "M", "э"), + (0x1E049, "M", "ю"), + (0x1E04A, "M", "ꚉ"), + (0x1E04B, "M", "ә"), + (0x1E04C, "M", "і"), + (0x1E04D, "M", "ј"), + (0x1E04E, "M", "ө"), + (0x1E04F, "M", "ү"), + (0x1E050, "M", "ӏ"), + (0x1E051, "M", "а"), + (0x1E052, "M", "б"), + (0x1E053, "M", "в"), + (0x1E054, "M", "г"), + (0x1E055, "M", "д"), + (0x1E056, "M", "е"), + (0x1E057, "M", "ж"), + (0x1E058, "M", "з"), + (0x1E059, "M", "и"), + (0x1E05A, "M", "к"), + (0x1E05B, "M", "л"), + (0x1E05C, "M", "о"), + (0x1E05D, "M", "п"), + (0x1E05E, "M", "с"), + (0x1E05F, "M", "у"), + (0x1E060, "M", "ф"), + (0x1E061, "M", "х"), + (0x1E062, "M", "ц"), + (0x1E063, "M", "ч"), + (0x1E064, "M", "ш"), + (0x1E065, "M", "ъ"), + (0x1E066, "M", "ы"), + (0x1E067, "M", "ґ"), + (0x1E068, "M", "і"), + (0x1E069, "M", "ѕ"), + (0x1E06A, "M", "џ"), + (0x1E06B, "M", "ҫ"), + (0x1E06C, "M", "ꙑ"), + (0x1E06D, "M", "ұ"), + (0x1E06E, "X"), + (0x1E08F, "V"), + (0x1E090, "X"), + (0x1E100, "V"), + (0x1E12D, "X"), + (0x1E130, "V"), + (0x1E13E, "X"), + (0x1E140, "V"), + (0x1E14A, "X"), + (0x1E14E, "V"), + (0x1E150, "X"), + (0x1E290, "V"), + (0x1E2AF, "X"), + (0x1E2C0, "V"), + (0x1E2FA, "X"), + (0x1E2FF, "V"), + (0x1E300, "X"), + (0x1E4D0, "V"), + (0x1E4FA, "X"), + (0x1E7E0, "V"), + (0x1E7E7, "X"), + (0x1E7E8, "V"), + (0x1E7EC, "X"), + (0x1E7ED, "V"), + (0x1E7EF, "X"), + (0x1E7F0, "V"), + (0x1E7FF, "X"), + (0x1E800, "V"), + (0x1E8C5, "X"), + (0x1E8C7, "V"), + (0x1E8D7, "X"), + (0x1E900, "M", "𞤢"), + (0x1E901, "M", "𞤣"), + (0x1E902, "M", "𞤤"), + (0x1E903, "M", "𞤥"), + (0x1E904, "M", "𞤦"), + (0x1E905, "M", "𞤧"), + (0x1E906, "M", "𞤨"), + (0x1E907, "M", "𞤩"), + (0x1E908, "M", "𞤪"), + (0x1E909, "M", "𞤫"), + (0x1E90A, "M", "𞤬"), + (0x1E90B, "M", "𞤭"), + (0x1E90C, "M", "𞤮"), + (0x1E90D, "M", "𞤯"), + ] + + +def _seg_72() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1E90E, "M", "𞤰"), + (0x1E90F, "M", "𞤱"), + (0x1E910, "M", "𞤲"), + (0x1E911, "M", "𞤳"), + (0x1E912, "M", "𞤴"), + (0x1E913, "M", "𞤵"), + (0x1E914, "M", "𞤶"), + (0x1E915, "M", "𞤷"), + (0x1E916, "M", "𞤸"), + (0x1E917, "M", "𞤹"), + (0x1E918, "M", "𞤺"), + (0x1E919, "M", "𞤻"), + (0x1E91A, "M", "𞤼"), + (0x1E91B, "M", "𞤽"), + (0x1E91C, "M", "𞤾"), + (0x1E91D, "M", "𞤿"), + (0x1E91E, "M", "𞥀"), + (0x1E91F, "M", "𞥁"), + (0x1E920, "M", "𞥂"), + (0x1E921, "M", "𞥃"), + (0x1E922, "V"), + (0x1E94C, "X"), + (0x1E950, "V"), + (0x1E95A, "X"), + (0x1E95E, "V"), + (0x1E960, "X"), + (0x1EC71, "V"), + (0x1ECB5, "X"), + (0x1ED01, "V"), + (0x1ED3E, "X"), + (0x1EE00, "M", "ا"), + (0x1EE01, "M", "ب"), + (0x1EE02, "M", "ج"), + (0x1EE03, "M", "د"), + (0x1EE04, "X"), + (0x1EE05, "M", "و"), + (0x1EE06, "M", "ز"), + (0x1EE07, "M", "ح"), + (0x1EE08, "M", "ط"), + (0x1EE09, "M", "ي"), + (0x1EE0A, "M", "ك"), + (0x1EE0B, "M", "ل"), + (0x1EE0C, "M", "م"), + (0x1EE0D, "M", "ن"), + (0x1EE0E, "M", "س"), + (0x1EE0F, "M", "ع"), + (0x1EE10, "M", "ف"), + (0x1EE11, "M", "ص"), + (0x1EE12, "M", "ق"), + (0x1EE13, "M", "ر"), + (0x1EE14, "M", "ش"), + (0x1EE15, "M", "ت"), + (0x1EE16, "M", "ث"), + (0x1EE17, "M", "خ"), + (0x1EE18, "M", "ذ"), + (0x1EE19, "M", "ض"), + (0x1EE1A, "M", "ظ"), + (0x1EE1B, "M", "غ"), + (0x1EE1C, "M", "ٮ"), + (0x1EE1D, "M", "ں"), + (0x1EE1E, "M", "ڡ"), + (0x1EE1F, "M", "ٯ"), + (0x1EE20, "X"), + (0x1EE21, "M", "ب"), + (0x1EE22, "M", "ج"), + (0x1EE23, "X"), + (0x1EE24, "M", "ه"), + (0x1EE25, "X"), + (0x1EE27, "M", "ح"), + (0x1EE28, "X"), + (0x1EE29, "M", "ي"), + (0x1EE2A, "M", "ك"), + (0x1EE2B, "M", "ل"), + (0x1EE2C, "M", "م"), + (0x1EE2D, "M", "ن"), + (0x1EE2E, "M", "س"), + (0x1EE2F, "M", "ع"), + (0x1EE30, "M", "ف"), + (0x1EE31, "M", "ص"), + (0x1EE32, "M", "ق"), + (0x1EE33, "X"), + (0x1EE34, "M", "ش"), + (0x1EE35, "M", "ت"), + (0x1EE36, "M", "ث"), + (0x1EE37, "M", "خ"), + (0x1EE38, "X"), + (0x1EE39, "M", "ض"), + (0x1EE3A, "X"), + (0x1EE3B, "M", "غ"), + (0x1EE3C, "X"), + (0x1EE42, "M", "ج"), + (0x1EE43, "X"), + (0x1EE47, "M", "ح"), + (0x1EE48, "X"), + (0x1EE49, "M", "ي"), + (0x1EE4A, "X"), + (0x1EE4B, "M", "ل"), + (0x1EE4C, "X"), + (0x1EE4D, "M", "ن"), + (0x1EE4E, "M", "س"), + ] + + +def _seg_73() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1EE4F, "M", "ع"), + (0x1EE50, "X"), + (0x1EE51, "M", "ص"), + (0x1EE52, "M", "ق"), + (0x1EE53, "X"), + (0x1EE54, "M", "ش"), + (0x1EE55, "X"), + (0x1EE57, "M", "خ"), + (0x1EE58, "X"), + (0x1EE59, "M", "ض"), + (0x1EE5A, "X"), + (0x1EE5B, "M", "غ"), + (0x1EE5C, "X"), + (0x1EE5D, "M", "ں"), + (0x1EE5E, "X"), + (0x1EE5F, "M", "ٯ"), + (0x1EE60, "X"), + (0x1EE61, "M", "ب"), + (0x1EE62, "M", "ج"), + (0x1EE63, "X"), + (0x1EE64, "M", "ه"), + (0x1EE65, "X"), + (0x1EE67, "M", "ح"), + (0x1EE68, "M", "ط"), + (0x1EE69, "M", "ي"), + (0x1EE6A, "M", "ك"), + (0x1EE6B, "X"), + (0x1EE6C, "M", "م"), + (0x1EE6D, "M", "ن"), + (0x1EE6E, "M", "س"), + (0x1EE6F, "M", "ع"), + (0x1EE70, "M", "ف"), + (0x1EE71, "M", "ص"), + (0x1EE72, "M", "ق"), + (0x1EE73, "X"), + (0x1EE74, "M", "ش"), + (0x1EE75, "M", "ت"), + (0x1EE76, "M", "ث"), + (0x1EE77, "M", "خ"), + (0x1EE78, "X"), + (0x1EE79, "M", "ض"), + (0x1EE7A, "M", "ظ"), + (0x1EE7B, "M", "غ"), + (0x1EE7C, "M", "ٮ"), + (0x1EE7D, "X"), + (0x1EE7E, "M", "ڡ"), + (0x1EE7F, "X"), + (0x1EE80, "M", "ا"), + (0x1EE81, "M", "ب"), + (0x1EE82, "M", "ج"), + (0x1EE83, "M", "د"), + (0x1EE84, "M", "ه"), + (0x1EE85, "M", "و"), + (0x1EE86, "M", "ز"), + (0x1EE87, "M", "ح"), + (0x1EE88, "M", "ط"), + (0x1EE89, "M", "ي"), + (0x1EE8A, "X"), + (0x1EE8B, "M", "ل"), + (0x1EE8C, "M", "م"), + (0x1EE8D, "M", "ن"), + (0x1EE8E, "M", "س"), + (0x1EE8F, "M", "ع"), + (0x1EE90, "M", "ف"), + (0x1EE91, "M", "ص"), + (0x1EE92, "M", "ق"), + (0x1EE93, "M", "ر"), + (0x1EE94, "M", "ش"), + (0x1EE95, "M", "ت"), + (0x1EE96, "M", "ث"), + (0x1EE97, "M", "خ"), + (0x1EE98, "M", "ذ"), + (0x1EE99, "M", "ض"), + (0x1EE9A, "M", "ظ"), + (0x1EE9B, "M", "غ"), + (0x1EE9C, "X"), + (0x1EEA1, "M", "ب"), + (0x1EEA2, "M", "ج"), + (0x1EEA3, "M", "د"), + (0x1EEA4, "X"), + (0x1EEA5, "M", "و"), + (0x1EEA6, "M", "ز"), + (0x1EEA7, "M", "ح"), + (0x1EEA8, "M", "ط"), + (0x1EEA9, "M", "ي"), + (0x1EEAA, "X"), + (0x1EEAB, "M", "ل"), + (0x1EEAC, "M", "م"), + (0x1EEAD, "M", "ن"), + (0x1EEAE, "M", "س"), + (0x1EEAF, "M", "ع"), + (0x1EEB0, "M", "ف"), + (0x1EEB1, "M", "ص"), + (0x1EEB2, "M", "ق"), + (0x1EEB3, "M", "ر"), + (0x1EEB4, "M", "ش"), + (0x1EEB5, "M", "ت"), + (0x1EEB6, "M", "ث"), + (0x1EEB7, "M", "خ"), + (0x1EEB8, "M", "ذ"), + ] + + +def _seg_74() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1EEB9, "M", "ض"), + (0x1EEBA, "M", "ظ"), + (0x1EEBB, "M", "غ"), + (0x1EEBC, "X"), + (0x1EEF0, "V"), + (0x1EEF2, "X"), + (0x1F000, "V"), + (0x1F02C, "X"), + (0x1F030, "V"), + (0x1F094, "X"), + (0x1F0A0, "V"), + (0x1F0AF, "X"), + (0x1F0B1, "V"), + (0x1F0C0, "X"), + (0x1F0C1, "V"), + (0x1F0D0, "X"), + (0x1F0D1, "V"), + (0x1F0F6, "X"), + (0x1F101, "3", "0,"), + (0x1F102, "3", "1,"), + (0x1F103, "3", "2,"), + (0x1F104, "3", "3,"), + (0x1F105, "3", "4,"), + (0x1F106, "3", "5,"), + (0x1F107, "3", "6,"), + (0x1F108, "3", "7,"), + (0x1F109, "3", "8,"), + (0x1F10A, "3", "9,"), + (0x1F10B, "V"), + (0x1F110, "3", "(a)"), + (0x1F111, "3", "(b)"), + (0x1F112, "3", "(c)"), + (0x1F113, "3", "(d)"), + (0x1F114, "3", "(e)"), + (0x1F115, "3", "(f)"), + (0x1F116, "3", "(g)"), + (0x1F117, "3", "(h)"), + (0x1F118, "3", "(i)"), + (0x1F119, "3", "(j)"), + (0x1F11A, "3", "(k)"), + (0x1F11B, "3", "(l)"), + (0x1F11C, "3", "(m)"), + (0x1F11D, "3", "(n)"), + (0x1F11E, "3", "(o)"), + (0x1F11F, "3", "(p)"), + (0x1F120, "3", "(q)"), + (0x1F121, "3", "(r)"), + (0x1F122, "3", "(s)"), + (0x1F123, "3", "(t)"), + (0x1F124, "3", "(u)"), + (0x1F125, "3", "(v)"), + (0x1F126, "3", "(w)"), + (0x1F127, "3", "(x)"), + (0x1F128, "3", "(y)"), + (0x1F129, "3", "(z)"), + (0x1F12A, "M", "〔s〕"), + (0x1F12B, "M", "c"), + (0x1F12C, "M", "r"), + (0x1F12D, "M", "cd"), + (0x1F12E, "M", "wz"), + (0x1F12F, "V"), + (0x1F130, "M", "a"), + (0x1F131, "M", "b"), + (0x1F132, "M", "c"), + (0x1F133, "M", "d"), + (0x1F134, "M", "e"), + (0x1F135, "M", "f"), + (0x1F136, "M", "g"), + (0x1F137, "M", "h"), + (0x1F138, "M", "i"), + (0x1F139, "M", "j"), + (0x1F13A, "M", "k"), + (0x1F13B, "M", "l"), + (0x1F13C, "M", "m"), + (0x1F13D, "M", "n"), + (0x1F13E, "M", "o"), + (0x1F13F, "M", "p"), + (0x1F140, "M", "q"), + (0x1F141, "M", "r"), + (0x1F142, "M", "s"), + (0x1F143, "M", "t"), + (0x1F144, "M", "u"), + (0x1F145, "M", "v"), + (0x1F146, "M", "w"), + (0x1F147, "M", "x"), + (0x1F148, "M", "y"), + (0x1F149, "M", "z"), + (0x1F14A, "M", "hv"), + (0x1F14B, "M", "mv"), + (0x1F14C, "M", "sd"), + (0x1F14D, "M", "ss"), + (0x1F14E, "M", "ppv"), + (0x1F14F, "M", "wc"), + (0x1F150, "V"), + (0x1F16A, "M", "mc"), + (0x1F16B, "M", "md"), + (0x1F16C, "M", "mr"), + (0x1F16D, "V"), + (0x1F190, "M", "dj"), + (0x1F191, "V"), + ] + + +def _seg_75() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1F1AE, "X"), + (0x1F1E6, "V"), + (0x1F200, "M", "ほか"), + (0x1F201, "M", "ココ"), + (0x1F202, "M", "サ"), + (0x1F203, "X"), + (0x1F210, "M", "手"), + (0x1F211, "M", "字"), + (0x1F212, "M", "双"), + (0x1F213, "M", "デ"), + (0x1F214, "M", "二"), + (0x1F215, "M", "多"), + (0x1F216, "M", "解"), + (0x1F217, "M", "天"), + (0x1F218, "M", "交"), + (0x1F219, "M", "映"), + (0x1F21A, "M", "無"), + (0x1F21B, "M", "料"), + (0x1F21C, "M", "前"), + (0x1F21D, "M", "後"), + (0x1F21E, "M", "再"), + (0x1F21F, "M", "新"), + (0x1F220, "M", "初"), + (0x1F221, "M", "終"), + (0x1F222, "M", "生"), + (0x1F223, "M", "販"), + (0x1F224, "M", "声"), + (0x1F225, "M", "吹"), + (0x1F226, "M", "演"), + (0x1F227, "M", "投"), + (0x1F228, "M", "捕"), + (0x1F229, "M", "一"), + (0x1F22A, "M", "三"), + (0x1F22B, "M", "遊"), + (0x1F22C, "M", "左"), + (0x1F22D, "M", "中"), + (0x1F22E, "M", "右"), + (0x1F22F, "M", "指"), + (0x1F230, "M", "走"), + (0x1F231, "M", "打"), + (0x1F232, "M", "禁"), + (0x1F233, "M", "空"), + (0x1F234, "M", "合"), + (0x1F235, "M", "満"), + (0x1F236, "M", "有"), + (0x1F237, "M", "月"), + (0x1F238, "M", "申"), + (0x1F239, "M", "割"), + (0x1F23A, "M", "営"), + (0x1F23B, "M", "配"), + (0x1F23C, "X"), + (0x1F240, "M", "〔本〕"), + (0x1F241, "M", "〔三〕"), + (0x1F242, "M", "〔二〕"), + (0x1F243, "M", "〔安〕"), + (0x1F244, "M", "〔点〕"), + (0x1F245, "M", "〔打〕"), + (0x1F246, "M", "〔盗〕"), + (0x1F247, "M", "〔勝〕"), + (0x1F248, "M", "〔敗〕"), + (0x1F249, "X"), + (0x1F250, "M", "得"), + (0x1F251, "M", "可"), + (0x1F252, "X"), + (0x1F260, "V"), + (0x1F266, "X"), + (0x1F300, "V"), + (0x1F6D8, "X"), + (0x1F6DC, "V"), + (0x1F6ED, "X"), + (0x1F6F0, "V"), + (0x1F6FD, "X"), + (0x1F700, "V"), + (0x1F777, "X"), + (0x1F77B, "V"), + (0x1F7DA, "X"), + (0x1F7E0, "V"), + (0x1F7EC, "X"), + (0x1F7F0, "V"), + (0x1F7F1, "X"), + (0x1F800, "V"), + (0x1F80C, "X"), + (0x1F810, "V"), + (0x1F848, "X"), + (0x1F850, "V"), + (0x1F85A, "X"), + (0x1F860, "V"), + (0x1F888, "X"), + (0x1F890, "V"), + (0x1F8AE, "X"), + (0x1F8B0, "V"), + (0x1F8B2, "X"), + (0x1F900, "V"), + (0x1FA54, "X"), + (0x1FA60, "V"), + (0x1FA6E, "X"), + (0x1FA70, "V"), + (0x1FA7D, "X"), + (0x1FA80, "V"), + (0x1FA89, "X"), + ] + + +def _seg_76() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1FA90, "V"), + (0x1FABE, "X"), + (0x1FABF, "V"), + (0x1FAC6, "X"), + (0x1FACE, "V"), + (0x1FADC, "X"), + (0x1FAE0, "V"), + (0x1FAE9, "X"), + (0x1FAF0, "V"), + (0x1FAF9, "X"), + (0x1FB00, "V"), + (0x1FB93, "X"), + (0x1FB94, "V"), + (0x1FBCB, "X"), + (0x1FBF0, "M", "0"), + (0x1FBF1, "M", "1"), + (0x1FBF2, "M", "2"), + (0x1FBF3, "M", "3"), + (0x1FBF4, "M", "4"), + (0x1FBF5, "M", "5"), + (0x1FBF6, "M", "6"), + (0x1FBF7, "M", "7"), + (0x1FBF8, "M", "8"), + (0x1FBF9, "M", "9"), + (0x1FBFA, "X"), + (0x20000, "V"), + (0x2A6E0, "X"), + (0x2A700, "V"), + (0x2B73A, "X"), + (0x2B740, "V"), + (0x2B81E, "X"), + (0x2B820, "V"), + (0x2CEA2, "X"), + (0x2CEB0, "V"), + (0x2EBE1, "X"), + (0x2EBF0, "V"), + (0x2EE5E, "X"), + (0x2F800, "M", "丽"), + (0x2F801, "M", "丸"), + (0x2F802, "M", "乁"), + (0x2F803, "M", "𠄢"), + (0x2F804, "M", "你"), + (0x2F805, "M", "侮"), + (0x2F806, "M", "侻"), + (0x2F807, "M", "倂"), + (0x2F808, "M", "偺"), + (0x2F809, "M", "備"), + (0x2F80A, "M", "僧"), + (0x2F80B, "M", "像"), + (0x2F80C, "M", "㒞"), + (0x2F80D, "M", "𠘺"), + (0x2F80E, "M", "免"), + (0x2F80F, "M", "兔"), + (0x2F810, "M", "兤"), + (0x2F811, "M", "具"), + (0x2F812, "M", "𠔜"), + (0x2F813, "M", "㒹"), + (0x2F814, "M", "內"), + (0x2F815, "M", "再"), + (0x2F816, "M", "𠕋"), + (0x2F817, "M", "冗"), + (0x2F818, "M", "冤"), + (0x2F819, "M", "仌"), + (0x2F81A, "M", "冬"), + (0x2F81B, "M", "况"), + (0x2F81C, "M", "𩇟"), + (0x2F81D, "M", "凵"), + (0x2F81E, "M", "刃"), + (0x2F81F, "M", "㓟"), + (0x2F820, "M", "刻"), + (0x2F821, "M", "剆"), + (0x2F822, "M", "割"), + (0x2F823, "M", "剷"), + (0x2F824, "M", "㔕"), + (0x2F825, "M", "勇"), + (0x2F826, "M", "勉"), + (0x2F827, "M", "勤"), + (0x2F828, "M", "勺"), + (0x2F829, "M", "包"), + (0x2F82A, "M", "匆"), + (0x2F82B, "M", "北"), + (0x2F82C, "M", "卉"), + (0x2F82D, "M", "卑"), + (0x2F82E, "M", "博"), + (0x2F82F, "M", "即"), + (0x2F830, "M", "卽"), + (0x2F831, "M", "卿"), + (0x2F834, "M", "𠨬"), + (0x2F835, "M", "灰"), + (0x2F836, "M", "及"), + (0x2F837, "M", "叟"), + (0x2F838, "M", "𠭣"), + (0x2F839, "M", "叫"), + (0x2F83A, "M", "叱"), + (0x2F83B, "M", "吆"), + (0x2F83C, "M", "咞"), + (0x2F83D, "M", "吸"), + (0x2F83E, "M", "呈"), + (0x2F83F, "M", "周"), + (0x2F840, "M", "咢"), + ] + + +def _seg_77() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x2F841, "M", "哶"), + (0x2F842, "M", "唐"), + (0x2F843, "M", "啓"), + (0x2F844, "M", "啣"), + (0x2F845, "M", "善"), + (0x2F847, "M", "喙"), + (0x2F848, "M", "喫"), + (0x2F849, "M", "喳"), + (0x2F84A, "M", "嗂"), + (0x2F84B, "M", "圖"), + (0x2F84C, "M", "嘆"), + (0x2F84D, "M", "圗"), + (0x2F84E, "M", "噑"), + (0x2F84F, "M", "噴"), + (0x2F850, "M", "切"), + (0x2F851, "M", "壮"), + (0x2F852, "M", "城"), + (0x2F853, "M", "埴"), + (0x2F854, "M", "堍"), + (0x2F855, "M", "型"), + (0x2F856, "M", "堲"), + (0x2F857, "M", "報"), + (0x2F858, "M", "墬"), + (0x2F859, "M", "𡓤"), + (0x2F85A, "M", "売"), + (0x2F85B, "M", "壷"), + (0x2F85C, "M", "夆"), + (0x2F85D, "M", "多"), + (0x2F85E, "M", "夢"), + (0x2F85F, "M", "奢"), + (0x2F860, "M", "𡚨"), + (0x2F861, "M", "𡛪"), + (0x2F862, "M", "姬"), + (0x2F863, "M", "娛"), + (0x2F864, "M", "娧"), + (0x2F865, "M", "姘"), + (0x2F866, "M", "婦"), + (0x2F867, "M", "㛮"), + (0x2F868, "X"), + (0x2F869, "M", "嬈"), + (0x2F86A, "M", "嬾"), + (0x2F86C, "M", "𡧈"), + (0x2F86D, "M", "寃"), + (0x2F86E, "M", "寘"), + (0x2F86F, "M", "寧"), + (0x2F870, "M", "寳"), + (0x2F871, "M", "𡬘"), + (0x2F872, "M", "寿"), + (0x2F873, "M", "将"), + (0x2F874, "X"), + (0x2F875, "M", "尢"), + (0x2F876, "M", "㞁"), + (0x2F877, "M", "屠"), + (0x2F878, "M", "屮"), + (0x2F879, "M", "峀"), + (0x2F87A, "M", "岍"), + (0x2F87B, "M", "𡷤"), + (0x2F87C, "M", "嵃"), + (0x2F87D, "M", "𡷦"), + (0x2F87E, "M", "嵮"), + (0x2F87F, "M", "嵫"), + (0x2F880, "M", "嵼"), + (0x2F881, "M", "巡"), + (0x2F882, "M", "巢"), + (0x2F883, "M", "㠯"), + (0x2F884, "M", "巽"), + (0x2F885, "M", "帨"), + (0x2F886, "M", "帽"), + (0x2F887, "M", "幩"), + (0x2F888, "M", "㡢"), + (0x2F889, "M", "𢆃"), + (0x2F88A, "M", "㡼"), + (0x2F88B, "M", "庰"), + (0x2F88C, "M", "庳"), + (0x2F88D, "M", "庶"), + (0x2F88E, "M", "廊"), + (0x2F88F, "M", "𪎒"), + (0x2F890, "M", "廾"), + (0x2F891, "M", "𢌱"), + (0x2F893, "M", "舁"), + (0x2F894, "M", "弢"), + (0x2F896, "M", "㣇"), + (0x2F897, "M", "𣊸"), + (0x2F898, "M", "𦇚"), + (0x2F899, "M", "形"), + (0x2F89A, "M", "彫"), + (0x2F89B, "M", "㣣"), + (0x2F89C, "M", "徚"), + (0x2F89D, "M", "忍"), + (0x2F89E, "M", "志"), + (0x2F89F, "M", "忹"), + (0x2F8A0, "M", "悁"), + (0x2F8A1, "M", "㤺"), + (0x2F8A2, "M", "㤜"), + (0x2F8A3, "M", "悔"), + (0x2F8A4, "M", "𢛔"), + (0x2F8A5, "M", "惇"), + (0x2F8A6, "M", "慈"), + (0x2F8A7, "M", "慌"), + (0x2F8A8, "M", "慎"), + ] + + +def _seg_78() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x2F8A9, "M", "慌"), + (0x2F8AA, "M", "慺"), + (0x2F8AB, "M", "憎"), + (0x2F8AC, "M", "憲"), + (0x2F8AD, "M", "憤"), + (0x2F8AE, "M", "憯"), + (0x2F8AF, "M", "懞"), + (0x2F8B0, "M", "懲"), + (0x2F8B1, "M", "懶"), + (0x2F8B2, "M", "成"), + (0x2F8B3, "M", "戛"), + (0x2F8B4, "M", "扝"), + (0x2F8B5, "M", "抱"), + (0x2F8B6, "M", "拔"), + (0x2F8B7, "M", "捐"), + (0x2F8B8, "M", "𢬌"), + (0x2F8B9, "M", "挽"), + (0x2F8BA, "M", "拼"), + (0x2F8BB, "M", "捨"), + (0x2F8BC, "M", "掃"), + (0x2F8BD, "M", "揤"), + (0x2F8BE, "M", "𢯱"), + (0x2F8BF, "M", "搢"), + (0x2F8C0, "M", "揅"), + (0x2F8C1, "M", "掩"), + (0x2F8C2, "M", "㨮"), + (0x2F8C3, "M", "摩"), + (0x2F8C4, "M", "摾"), + (0x2F8C5, "M", "撝"), + (0x2F8C6, "M", "摷"), + (0x2F8C7, "M", "㩬"), + (0x2F8C8, "M", "敏"), + (0x2F8C9, "M", "敬"), + (0x2F8CA, "M", "𣀊"), + (0x2F8CB, "M", "旣"), + (0x2F8CC, "M", "書"), + (0x2F8CD, "M", "晉"), + (0x2F8CE, "M", "㬙"), + (0x2F8CF, "M", "暑"), + (0x2F8D0, "M", "㬈"), + (0x2F8D1, "M", "㫤"), + (0x2F8D2, "M", "冒"), + (0x2F8D3, "M", "冕"), + (0x2F8D4, "M", "最"), + (0x2F8D5, "M", "暜"), + (0x2F8D6, "M", "肭"), + (0x2F8D7, "M", "䏙"), + (0x2F8D8, "M", "朗"), + (0x2F8D9, "M", "望"), + (0x2F8DA, "M", "朡"), + (0x2F8DB, "M", "杞"), + (0x2F8DC, "M", "杓"), + (0x2F8DD, "M", "𣏃"), + (0x2F8DE, "M", "㭉"), + (0x2F8DF, "M", "柺"), + (0x2F8E0, "M", "枅"), + (0x2F8E1, "M", "桒"), + (0x2F8E2, "M", "梅"), + (0x2F8E3, "M", "𣑭"), + (0x2F8E4, "M", "梎"), + (0x2F8E5, "M", "栟"), + (0x2F8E6, "M", "椔"), + (0x2F8E7, "M", "㮝"), + (0x2F8E8, "M", "楂"), + (0x2F8E9, "M", "榣"), + (0x2F8EA, "M", "槪"), + (0x2F8EB, "M", "檨"), + (0x2F8EC, "M", "𣚣"), + (0x2F8ED, "M", "櫛"), + (0x2F8EE, "M", "㰘"), + (0x2F8EF, "M", "次"), + (0x2F8F0, "M", "𣢧"), + (0x2F8F1, "M", "歔"), + (0x2F8F2, "M", "㱎"), + (0x2F8F3, "M", "歲"), + (0x2F8F4, "M", "殟"), + (0x2F8F5, "M", "殺"), + (0x2F8F6, "M", "殻"), + (0x2F8F7, "M", "𣪍"), + (0x2F8F8, "M", "𡴋"), + (0x2F8F9, "M", "𣫺"), + (0x2F8FA, "M", "汎"), + (0x2F8FB, "M", "𣲼"), + (0x2F8FC, "M", "沿"), + (0x2F8FD, "M", "泍"), + (0x2F8FE, "M", "汧"), + (0x2F8FF, "M", "洖"), + (0x2F900, "M", "派"), + (0x2F901, "M", "海"), + (0x2F902, "M", "流"), + (0x2F903, "M", "浩"), + (0x2F904, "M", "浸"), + (0x2F905, "M", "涅"), + (0x2F906, "M", "𣴞"), + (0x2F907, "M", "洴"), + (0x2F908, "M", "港"), + (0x2F909, "M", "湮"), + (0x2F90A, "M", "㴳"), + (0x2F90B, "M", "滋"), + (0x2F90C, "M", "滇"), + ] + + +def _seg_79() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x2F90D, "M", "𣻑"), + (0x2F90E, "M", "淹"), + (0x2F90F, "M", "潮"), + (0x2F910, "M", "𣽞"), + (0x2F911, "M", "𣾎"), + (0x2F912, "M", "濆"), + (0x2F913, "M", "瀹"), + (0x2F914, "M", "瀞"), + (0x2F915, "M", "瀛"), + (0x2F916, "M", "㶖"), + (0x2F917, "M", "灊"), + (0x2F918, "M", "災"), + (0x2F919, "M", "灷"), + (0x2F91A, "M", "炭"), + (0x2F91B, "M", "𠔥"), + (0x2F91C, "M", "煅"), + (0x2F91D, "M", "𤉣"), + (0x2F91E, "M", "熜"), + (0x2F91F, "X"), + (0x2F920, "M", "爨"), + (0x2F921, "M", "爵"), + (0x2F922, "M", "牐"), + (0x2F923, "M", "𤘈"), + (0x2F924, "M", "犀"), + (0x2F925, "M", "犕"), + (0x2F926, "M", "𤜵"), + (0x2F927, "M", "𤠔"), + (0x2F928, "M", "獺"), + (0x2F929, "M", "王"), + (0x2F92A, "M", "㺬"), + (0x2F92B, "M", "玥"), + (0x2F92C, "M", "㺸"), + (0x2F92E, "M", "瑇"), + (0x2F92F, "M", "瑜"), + (0x2F930, "M", "瑱"), + (0x2F931, "M", "璅"), + (0x2F932, "M", "瓊"), + (0x2F933, "M", "㼛"), + (0x2F934, "M", "甤"), + (0x2F935, "M", "𤰶"), + (0x2F936, "M", "甾"), + (0x2F937, "M", "𤲒"), + (0x2F938, "M", "異"), + (0x2F939, "M", "𢆟"), + (0x2F93A, "M", "瘐"), + (0x2F93B, "M", "𤾡"), + (0x2F93C, "M", "𤾸"), + (0x2F93D, "M", "𥁄"), + (0x2F93E, "M", "㿼"), + (0x2F93F, "M", "䀈"), + (0x2F940, "M", "直"), + (0x2F941, "M", "𥃳"), + (0x2F942, "M", "𥃲"), + (0x2F943, "M", "𥄙"), + (0x2F944, "M", "𥄳"), + (0x2F945, "M", "眞"), + (0x2F946, "M", "真"), + (0x2F948, "M", "睊"), + (0x2F949, "M", "䀹"), + (0x2F94A, "M", "瞋"), + (0x2F94B, "M", "䁆"), + (0x2F94C, "M", "䂖"), + (0x2F94D, "M", "𥐝"), + (0x2F94E, "M", "硎"), + (0x2F94F, "M", "碌"), + (0x2F950, "M", "磌"), + (0x2F951, "M", "䃣"), + (0x2F952, "M", "𥘦"), + (0x2F953, "M", "祖"), + (0x2F954, "M", "𥚚"), + (0x2F955, "M", "𥛅"), + (0x2F956, "M", "福"), + (0x2F957, "M", "秫"), + (0x2F958, "M", "䄯"), + (0x2F959, "M", "穀"), + (0x2F95A, "M", "穊"), + (0x2F95B, "M", "穏"), + (0x2F95C, "M", "𥥼"), + (0x2F95D, "M", "𥪧"), + (0x2F95F, "X"), + (0x2F960, "M", "䈂"), + (0x2F961, "M", "𥮫"), + (0x2F962, "M", "篆"), + (0x2F963, "M", "築"), + (0x2F964, "M", "䈧"), + (0x2F965, "M", "𥲀"), + (0x2F966, "M", "糒"), + (0x2F967, "M", "䊠"), + (0x2F968, "M", "糨"), + (0x2F969, "M", "糣"), + (0x2F96A, "M", "紀"), + (0x2F96B, "M", "𥾆"), + (0x2F96C, "M", "絣"), + (0x2F96D, "M", "䌁"), + (0x2F96E, "M", "緇"), + (0x2F96F, "M", "縂"), + (0x2F970, "M", "繅"), + (0x2F971, "M", "䌴"), + (0x2F972, "M", "𦈨"), + (0x2F973, "M", "𦉇"), + ] + + +def _seg_80() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x2F974, "M", "䍙"), + (0x2F975, "M", "𦋙"), + (0x2F976, "M", "罺"), + (0x2F977, "M", "𦌾"), + (0x2F978, "M", "羕"), + (0x2F979, "M", "翺"), + (0x2F97A, "M", "者"), + (0x2F97B, "M", "𦓚"), + (0x2F97C, "M", "𦔣"), + (0x2F97D, "M", "聠"), + (0x2F97E, "M", "𦖨"), + (0x2F97F, "M", "聰"), + (0x2F980, "M", "𣍟"), + (0x2F981, "M", "䏕"), + (0x2F982, "M", "育"), + (0x2F983, "M", "脃"), + (0x2F984, "M", "䐋"), + (0x2F985, "M", "脾"), + (0x2F986, "M", "媵"), + (0x2F987, "M", "𦞧"), + (0x2F988, "M", "𦞵"), + (0x2F989, "M", "𣎓"), + (0x2F98A, "M", "𣎜"), + (0x2F98B, "M", "舁"), + (0x2F98C, "M", "舄"), + (0x2F98D, "M", "辞"), + (0x2F98E, "M", "䑫"), + (0x2F98F, "M", "芑"), + (0x2F990, "M", "芋"), + (0x2F991, "M", "芝"), + (0x2F992, "M", "劳"), + (0x2F993, "M", "花"), + (0x2F994, "M", "芳"), + (0x2F995, "M", "芽"), + (0x2F996, "M", "苦"), + (0x2F997, "M", "𦬼"), + (0x2F998, "M", "若"), + (0x2F999, "M", "茝"), + (0x2F99A, "M", "荣"), + (0x2F99B, "M", "莭"), + (0x2F99C, "M", "茣"), + (0x2F99D, "M", "莽"), + (0x2F99E, "M", "菧"), + (0x2F99F, "M", "著"), + (0x2F9A0, "M", "荓"), + (0x2F9A1, "M", "菊"), + (0x2F9A2, "M", "菌"), + (0x2F9A3, "M", "菜"), + (0x2F9A4, "M", "𦰶"), + (0x2F9A5, "M", "𦵫"), + (0x2F9A6, "M", "𦳕"), + (0x2F9A7, "M", "䔫"), + (0x2F9A8, "M", "蓱"), + (0x2F9A9, "M", "蓳"), + (0x2F9AA, "M", "蔖"), + (0x2F9AB, "M", "𧏊"), + (0x2F9AC, "M", "蕤"), + (0x2F9AD, "M", "𦼬"), + (0x2F9AE, "M", "䕝"), + (0x2F9AF, "M", "䕡"), + (0x2F9B0, "M", "𦾱"), + (0x2F9B1, "M", "𧃒"), + (0x2F9B2, "M", "䕫"), + (0x2F9B3, "M", "虐"), + (0x2F9B4, "M", "虜"), + (0x2F9B5, "M", "虧"), + (0x2F9B6, "M", "虩"), + (0x2F9B7, "M", "蚩"), + (0x2F9B8, "M", "蚈"), + (0x2F9B9, "M", "蜎"), + (0x2F9BA, "M", "蛢"), + (0x2F9BB, "M", "蝹"), + (0x2F9BC, "M", "蜨"), + (0x2F9BD, "M", "蝫"), + (0x2F9BE, "M", "螆"), + (0x2F9BF, "X"), + (0x2F9C0, "M", "蟡"), + (0x2F9C1, "M", "蠁"), + (0x2F9C2, "M", "䗹"), + (0x2F9C3, "M", "衠"), + (0x2F9C4, "M", "衣"), + (0x2F9C5, "M", "𧙧"), + (0x2F9C6, "M", "裗"), + (0x2F9C7, "M", "裞"), + (0x2F9C8, "M", "䘵"), + (0x2F9C9, "M", "裺"), + (0x2F9CA, "M", "㒻"), + (0x2F9CB, "M", "𧢮"), + (0x2F9CC, "M", "𧥦"), + (0x2F9CD, "M", "䚾"), + (0x2F9CE, "M", "䛇"), + (0x2F9CF, "M", "誠"), + (0x2F9D0, "M", "諭"), + (0x2F9D1, "M", "變"), + (0x2F9D2, "M", "豕"), + (0x2F9D3, "M", "𧲨"), + (0x2F9D4, "M", "貫"), + (0x2F9D5, "M", "賁"), + (0x2F9D6, "M", "贛"), + (0x2F9D7, "M", "起"), + ] + + +def _seg_81() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x2F9D8, "M", "𧼯"), + (0x2F9D9, "M", "𠠄"), + (0x2F9DA, "M", "跋"), + (0x2F9DB, "M", "趼"), + (0x2F9DC, "M", "跰"), + (0x2F9DD, "M", "𠣞"), + (0x2F9DE, "M", "軔"), + (0x2F9DF, "M", "輸"), + (0x2F9E0, "M", "𨗒"), + (0x2F9E1, "M", "𨗭"), + (0x2F9E2, "M", "邔"), + (0x2F9E3, "M", "郱"), + (0x2F9E4, "M", "鄑"), + (0x2F9E5, "M", "𨜮"), + (0x2F9E6, "M", "鄛"), + (0x2F9E7, "M", "鈸"), + (0x2F9E8, "M", "鋗"), + (0x2F9E9, "M", "鋘"), + (0x2F9EA, "M", "鉼"), + (0x2F9EB, "M", "鏹"), + (0x2F9EC, "M", "鐕"), + (0x2F9ED, "M", "𨯺"), + (0x2F9EE, "M", "開"), + (0x2F9EF, "M", "䦕"), + (0x2F9F0, "M", "閷"), + (0x2F9F1, "M", "𨵷"), + (0x2F9F2, "M", "䧦"), + (0x2F9F3, "M", "雃"), + (0x2F9F4, "M", "嶲"), + (0x2F9F5, "M", "霣"), + (0x2F9F6, "M", "𩅅"), + (0x2F9F7, "M", "𩈚"), + (0x2F9F8, "M", "䩮"), + (0x2F9F9, "M", "䩶"), + (0x2F9FA, "M", "韠"), + (0x2F9FB, "M", "𩐊"), + (0x2F9FC, "M", "䪲"), + (0x2F9FD, "M", "𩒖"), + (0x2F9FE, "M", "頋"), + (0x2FA00, "M", "頩"), + (0x2FA01, "M", "𩖶"), + (0x2FA02, "M", "飢"), + (0x2FA03, "M", "䬳"), + (0x2FA04, "M", "餩"), + (0x2FA05, "M", "馧"), + (0x2FA06, "M", "駂"), + (0x2FA07, "M", "駾"), + (0x2FA08, "M", "䯎"), + (0x2FA09, "M", "𩬰"), + (0x2FA0A, "M", "鬒"), + (0x2FA0B, "M", "鱀"), + (0x2FA0C, "M", "鳽"), + (0x2FA0D, "M", "䳎"), + (0x2FA0E, "M", "䳭"), + (0x2FA0F, "M", "鵧"), + (0x2FA10, "M", "𪃎"), + (0x2FA11, "M", "䳸"), + (0x2FA12, "M", "𪄅"), + (0x2FA13, "M", "𪈎"), + (0x2FA14, "M", "𪊑"), + (0x2FA15, "M", "麻"), + (0x2FA16, "M", "䵖"), + (0x2FA17, "M", "黹"), + (0x2FA18, "M", "黾"), + (0x2FA19, "M", "鼅"), + (0x2FA1A, "M", "鼏"), + (0x2FA1B, "M", "鼖"), + (0x2FA1C, "M", "鼻"), + (0x2FA1D, "M", "𪘀"), + (0x2FA1E, "X"), + (0x30000, "V"), + (0x3134B, "X"), + (0x31350, "V"), + (0x323B0, "X"), + (0xE0100, "I"), + (0xE01F0, "X"), + ] + + +uts46data = tuple( + _seg_0() + + _seg_1() + + _seg_2() + + _seg_3() + + _seg_4() + + _seg_5() + + _seg_6() + + _seg_7() + + _seg_8() + + _seg_9() + + _seg_10() + + _seg_11() + + _seg_12() + + _seg_13() + + _seg_14() + + _seg_15() + + _seg_16() + + _seg_17() + + _seg_18() + + _seg_19() + + _seg_20() + + _seg_21() + + _seg_22() + + _seg_23() + + _seg_24() + + _seg_25() + + _seg_26() + + _seg_27() + + _seg_28() + + _seg_29() + + _seg_30() + + _seg_31() + + _seg_32() + + _seg_33() + + _seg_34() + + _seg_35() + + _seg_36() + + _seg_37() + + _seg_38() + + _seg_39() + + _seg_40() + + _seg_41() + + _seg_42() + + _seg_43() + + _seg_44() + + _seg_45() + + _seg_46() + + _seg_47() + + _seg_48() + + _seg_49() + + _seg_50() + + _seg_51() + + _seg_52() + + _seg_53() + + _seg_54() + + _seg_55() + + _seg_56() + + _seg_57() + + _seg_58() + + _seg_59() + + _seg_60() + + _seg_61() + + _seg_62() + + _seg_63() + + _seg_64() + + _seg_65() + + _seg_66() + + _seg_67() + + _seg_68() + + _seg_69() + + _seg_70() + + _seg_71() + + _seg_72() + + _seg_73() + + _seg_74() + + _seg_75() + + _seg_76() + + _seg_77() + + _seg_78() + + _seg_79() + + _seg_80() + + _seg_81() +) # type: Tuple[Union[Tuple[int, str], Tuple[int, str, str]], ...] diff --git a/venv/lib/python3.12/site-packages/jinja2-3.1.6.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/jinja2-3.1.6.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jinja2-3.1.6.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.12/site-packages/jinja2-3.1.6.dist-info/METADATA b/venv/lib/python3.12/site-packages/jinja2-3.1.6.dist-info/METADATA new file mode 100644 index 00000000..ffef2ff3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jinja2-3.1.6.dist-info/METADATA @@ -0,0 +1,84 @@ +Metadata-Version: 2.4 +Name: Jinja2 +Version: 3.1.6 +Summary: A very fast and expressive template engine. +Maintainer-email: Pallets +Requires-Python: >=3.7 +Description-Content-Type: text/markdown +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Text Processing :: Markup :: HTML +Classifier: Typing :: Typed +License-File: LICENSE.txt +Requires-Dist: MarkupSafe>=2.0 +Requires-Dist: Babel>=2.7 ; extra == "i18n" +Project-URL: Changes, https://jinja.palletsprojects.com/changes/ +Project-URL: Chat, https://discord.gg/pallets +Project-URL: Documentation, https://jinja.palletsprojects.com/ +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Source, https://github.com/pallets/jinja/ +Provides-Extra: i18n + +# Jinja + +Jinja is a fast, expressive, extensible templating engine. Special +placeholders in the template allow writing code similar to Python +syntax. Then the template is passed data to render the final document. + +It includes: + +- Template inheritance and inclusion. +- Define and import macros within templates. +- HTML templates can use autoescaping to prevent XSS from untrusted + user input. +- A sandboxed environment can safely render untrusted templates. +- AsyncIO support for generating templates and calling async + functions. +- I18N support with Babel. +- Templates are compiled to optimized Python code just-in-time and + cached, or can be compiled ahead-of-time. +- Exceptions point to the correct line in templates to make debugging + easier. +- Extensible filters, tests, functions, and even syntax. + +Jinja's philosophy is that while application logic belongs in Python if +possible, it shouldn't make the template designer's job difficult by +restricting functionality too much. + + +## In A Nutshell + +```jinja +{% extends "base.html" %} +{% block title %}Members{% endblock %} +{% block content %} + +{% endblock %} +``` + +## Donate + +The Pallets organization develops and supports Jinja and other popular +packages. In order to grow the community of contributors and users, and +allow the maintainers to devote more time to the projects, [please +donate today][]. + +[please donate today]: https://palletsprojects.com/donate + +## Contributing + +See our [detailed contributing documentation][contrib] for many ways to +contribute, including reporting issues, requesting features, asking or answering +questions, and making PRs. + +[contrib]: https://palletsprojects.com/contributing/ + diff --git a/venv/lib/python3.12/site-packages/jinja2-3.1.6.dist-info/RECORD b/venv/lib/python3.12/site-packages/jinja2-3.1.6.dist-info/RECORD new file mode 100644 index 00000000..ffa3866a --- /dev/null +++ b/venv/lib/python3.12/site-packages/jinja2-3.1.6.dist-info/RECORD @@ -0,0 +1,57 @@ +jinja2-3.1.6.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +jinja2-3.1.6.dist-info/METADATA,sha256=aMVUj7Z8QTKhOJjZsx7FDGvqKr3ZFdkh8hQ1XDpkmcg,2871 +jinja2-3.1.6.dist-info/RECORD,, +jinja2-3.1.6.dist-info/WHEEL,sha256=_2ozNFCLWc93bK4WKHCO-eDUENDlo-dgc9cU3qokYO4,82 +jinja2-3.1.6.dist-info/entry_points.txt,sha256=OL85gYU1eD8cuPlikifFngXpeBjaxl6rIJ8KkC_3r-I,58 +jinja2-3.1.6.dist-info/licenses/LICENSE.txt,sha256=O0nc7kEF6ze6wQ-vG-JgQI_oXSUrjp3y4JefweCUQ3s,1475 +jinja2/__init__.py,sha256=xxepO9i7DHsqkQrgBEduLtfoz2QCuT6_gbL4XSN1hbU,1928 +jinja2/__pycache__/__init__.cpython-312.pyc,, +jinja2/__pycache__/_identifier.cpython-312.pyc,, +jinja2/__pycache__/async_utils.cpython-312.pyc,, +jinja2/__pycache__/bccache.cpython-312.pyc,, +jinja2/__pycache__/compiler.cpython-312.pyc,, +jinja2/__pycache__/constants.cpython-312.pyc,, +jinja2/__pycache__/debug.cpython-312.pyc,, +jinja2/__pycache__/defaults.cpython-312.pyc,, +jinja2/__pycache__/environment.cpython-312.pyc,, +jinja2/__pycache__/exceptions.cpython-312.pyc,, +jinja2/__pycache__/ext.cpython-312.pyc,, +jinja2/__pycache__/filters.cpython-312.pyc,, +jinja2/__pycache__/idtracking.cpython-312.pyc,, +jinja2/__pycache__/lexer.cpython-312.pyc,, +jinja2/__pycache__/loaders.cpython-312.pyc,, +jinja2/__pycache__/meta.cpython-312.pyc,, +jinja2/__pycache__/nativetypes.cpython-312.pyc,, +jinja2/__pycache__/nodes.cpython-312.pyc,, +jinja2/__pycache__/optimizer.cpython-312.pyc,, +jinja2/__pycache__/parser.cpython-312.pyc,, +jinja2/__pycache__/runtime.cpython-312.pyc,, +jinja2/__pycache__/sandbox.cpython-312.pyc,, +jinja2/__pycache__/tests.cpython-312.pyc,, +jinja2/__pycache__/utils.cpython-312.pyc,, +jinja2/__pycache__/visitor.cpython-312.pyc,, +jinja2/_identifier.py,sha256=_zYctNKzRqlk_murTNlzrju1FFJL7Va_Ijqqd7ii2lU,1958 +jinja2/async_utils.py,sha256=vK-PdsuorOMnWSnEkT3iUJRIkTnYgO2T6MnGxDgHI5o,2834 +jinja2/bccache.py,sha256=gh0qs9rulnXo0PhX5jTJy2UHzI8wFnQ63o_vw7nhzRg,14061 +jinja2/compiler.py,sha256=9RpCQl5X88BHllJiPsHPh295Hh0uApvwFJNQuutULeM,74131 +jinja2/constants.py,sha256=GMoFydBF_kdpaRKPoM5cl5MviquVRLVyZtfp5-16jg0,1433 +jinja2/debug.py,sha256=CnHqCDHd-BVGvti_8ZsTolnXNhA3ECsY-6n_2pwU8Hw,6297 +jinja2/defaults.py,sha256=boBcSw78h-lp20YbaXSJsqkAI2uN_mD_TtCydpeq5wU,1267 +jinja2/environment.py,sha256=9nhrP7Ch-NbGX00wvyr4yy-uhNHq2OCc60ggGrni_fk,61513 +jinja2/exceptions.py,sha256=ioHeHrWwCWNaXX1inHmHVblvc4haO7AXsjCp3GfWvx0,5071 +jinja2/ext.py,sha256=5PF5eHfh8mXAIxXHHRB2xXbXohi8pE3nHSOxa66uS7E,31875 +jinja2/filters.py,sha256=PQ_Egd9n9jSgtnGQYyF4K5j2nYwhUIulhPnyimkdr-k,55212 +jinja2/idtracking.py,sha256=-ll5lIp73pML3ErUYiIJj7tdmWxcH_IlDv3yA_hiZYo,10555 +jinja2/lexer.py,sha256=LYiYio6br-Tep9nPcupWXsPEtjluw3p1mU-lNBVRUfk,29786 +jinja2/loaders.py,sha256=wIrnxjvcbqh5VwW28NSkfotiDq8qNCxIOSFbGUiSLB4,24055 +jinja2/meta.py,sha256=OTDPkaFvU2Hgvx-6akz7154F8BIWaRmvJcBFvwopHww,4397 +jinja2/nativetypes.py,sha256=7GIGALVJgdyL80oZJdQUaUfwSt5q2lSSZbXt0dNf_M4,4210 +jinja2/nodes.py,sha256=m1Duzcr6qhZI8JQ6VyJgUNinjAf5bQzijSmDnMsvUx8,34579 +jinja2/optimizer.py,sha256=rJnCRlQ7pZsEEmMhsQDgC_pKyDHxP5TPS6zVPGsgcu8,1651 +jinja2/parser.py,sha256=lLOFy3sEmHc5IaEHRiH1sQVnId2moUQzhyeJZTtdY30,40383 +jinja2/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +jinja2/runtime.py,sha256=gDk-GvdriJXqgsGbHgrcKTP0Yp6zPXzhzrIpCFH3jAU,34249 +jinja2/sandbox.py,sha256=Mw2aitlY2I8la7FYhcX2YG9BtUYcLnD0Gh3d29cDWrY,15009 +jinja2/tests.py,sha256=VLsBhVFnWg-PxSBz1MhRnNWgP1ovXk3neO1FLQMeC9Q,5926 +jinja2/utils.py,sha256=rRp3o9e7ZKS4fyrWRbELyLcpuGVTFcnooaOa1qx_FIk,24129 +jinja2/visitor.py,sha256=EcnL1PIwf_4RVCOMxsRNuR8AXHbS1qfAdMOE2ngKJz4,3557 diff --git a/venv/lib/python3.12/site-packages/jinja2-3.1.6.dist-info/WHEEL b/venv/lib/python3.12/site-packages/jinja2-3.1.6.dist-info/WHEEL new file mode 100644 index 00000000..23d2d7e9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jinja2-3.1.6.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: flit 3.11.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/venv/lib/python3.12/site-packages/jinja2-3.1.6.dist-info/entry_points.txt b/venv/lib/python3.12/site-packages/jinja2-3.1.6.dist-info/entry_points.txt new file mode 100644 index 00000000..abc3eae3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jinja2-3.1.6.dist-info/entry_points.txt @@ -0,0 +1,3 @@ +[babel.extractors] +jinja2=jinja2.ext:babel_extract[i18n] + diff --git a/venv/lib/python3.12/site-packages/jinja2-3.1.6.dist-info/licenses/LICENSE.txt b/venv/lib/python3.12/site-packages/jinja2-3.1.6.dist-info/licenses/LICENSE.txt new file mode 100644 index 00000000..c37cae49 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jinja2-3.1.6.dist-info/licenses/LICENSE.txt @@ -0,0 +1,28 @@ +Copyright 2007 Pallets + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/lib/python3.12/site-packages/jinja2/__init__.py b/venv/lib/python3.12/site-packages/jinja2/__init__.py new file mode 100644 index 00000000..1a423a3e --- /dev/null +++ b/venv/lib/python3.12/site-packages/jinja2/__init__.py @@ -0,0 +1,38 @@ +"""Jinja is a template engine written in pure Python. It provides a +non-XML syntax that supports inline expressions and an optional +sandboxed environment. +""" + +from .bccache import BytecodeCache as BytecodeCache +from .bccache import FileSystemBytecodeCache as FileSystemBytecodeCache +from .bccache import MemcachedBytecodeCache as MemcachedBytecodeCache +from .environment import Environment as Environment +from .environment import Template as Template +from .exceptions import TemplateAssertionError as TemplateAssertionError +from .exceptions import TemplateError as TemplateError +from .exceptions import TemplateNotFound as TemplateNotFound +from .exceptions import TemplateRuntimeError as TemplateRuntimeError +from .exceptions import TemplatesNotFound as TemplatesNotFound +from .exceptions import TemplateSyntaxError as TemplateSyntaxError +from .exceptions import UndefinedError as UndefinedError +from .loaders import BaseLoader as BaseLoader +from .loaders import ChoiceLoader as ChoiceLoader +from .loaders import DictLoader as DictLoader +from .loaders import FileSystemLoader as FileSystemLoader +from .loaders import FunctionLoader as FunctionLoader +from .loaders import ModuleLoader as ModuleLoader +from .loaders import PackageLoader as PackageLoader +from .loaders import PrefixLoader as PrefixLoader +from .runtime import ChainableUndefined as ChainableUndefined +from .runtime import DebugUndefined as DebugUndefined +from .runtime import make_logging_undefined as make_logging_undefined +from .runtime import StrictUndefined as StrictUndefined +from .runtime import Undefined as Undefined +from .utils import clear_caches as clear_caches +from .utils import is_undefined as is_undefined +from .utils import pass_context as pass_context +from .utils import pass_environment as pass_environment +from .utils import pass_eval_context as pass_eval_context +from .utils import select_autoescape as select_autoescape + +__version__ = "3.1.6" diff --git a/venv/lib/python3.12/site-packages/jinja2/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/jinja2/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 00000000..eeed2913 Binary files /dev/null and b/venv/lib/python3.12/site-packages/jinja2/__pycache__/__init__.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jinja2/__pycache__/_identifier.cpython-312.pyc b/venv/lib/python3.12/site-packages/jinja2/__pycache__/_identifier.cpython-312.pyc new file mode 100644 index 00000000..44a818d0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/jinja2/__pycache__/_identifier.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jinja2/__pycache__/async_utils.cpython-312.pyc b/venv/lib/python3.12/site-packages/jinja2/__pycache__/async_utils.cpython-312.pyc new file mode 100644 index 00000000..c00f4a5a Binary files /dev/null and b/venv/lib/python3.12/site-packages/jinja2/__pycache__/async_utils.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jinja2/__pycache__/bccache.cpython-312.pyc b/venv/lib/python3.12/site-packages/jinja2/__pycache__/bccache.cpython-312.pyc new file mode 100644 index 00000000..8ec1518e Binary files /dev/null and b/venv/lib/python3.12/site-packages/jinja2/__pycache__/bccache.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jinja2/__pycache__/compiler.cpython-312.pyc b/venv/lib/python3.12/site-packages/jinja2/__pycache__/compiler.cpython-312.pyc new file mode 100644 index 00000000..db1eaf9e Binary files /dev/null and b/venv/lib/python3.12/site-packages/jinja2/__pycache__/compiler.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jinja2/__pycache__/constants.cpython-312.pyc b/venv/lib/python3.12/site-packages/jinja2/__pycache__/constants.cpython-312.pyc new file mode 100644 index 00000000..b4efbfc7 Binary files /dev/null and b/venv/lib/python3.12/site-packages/jinja2/__pycache__/constants.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jinja2/__pycache__/debug.cpython-312.pyc b/venv/lib/python3.12/site-packages/jinja2/__pycache__/debug.cpython-312.pyc new file mode 100644 index 00000000..c3da4a2b Binary files /dev/null and b/venv/lib/python3.12/site-packages/jinja2/__pycache__/debug.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jinja2/__pycache__/defaults.cpython-312.pyc b/venv/lib/python3.12/site-packages/jinja2/__pycache__/defaults.cpython-312.pyc new file mode 100644 index 00000000..875cc9fc Binary files /dev/null and b/venv/lib/python3.12/site-packages/jinja2/__pycache__/defaults.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jinja2/__pycache__/environment.cpython-312.pyc b/venv/lib/python3.12/site-packages/jinja2/__pycache__/environment.cpython-312.pyc new file mode 100644 index 00000000..a54c2686 Binary files /dev/null and b/venv/lib/python3.12/site-packages/jinja2/__pycache__/environment.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jinja2/__pycache__/exceptions.cpython-312.pyc b/venv/lib/python3.12/site-packages/jinja2/__pycache__/exceptions.cpython-312.pyc new file mode 100644 index 00000000..63fb6ff8 Binary files /dev/null and b/venv/lib/python3.12/site-packages/jinja2/__pycache__/exceptions.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jinja2/__pycache__/ext.cpython-312.pyc b/venv/lib/python3.12/site-packages/jinja2/__pycache__/ext.cpython-312.pyc new file mode 100644 index 00000000..2ccefcec Binary files /dev/null and b/venv/lib/python3.12/site-packages/jinja2/__pycache__/ext.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jinja2/__pycache__/filters.cpython-312.pyc b/venv/lib/python3.12/site-packages/jinja2/__pycache__/filters.cpython-312.pyc new file mode 100644 index 00000000..5002a39e Binary files /dev/null and b/venv/lib/python3.12/site-packages/jinja2/__pycache__/filters.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jinja2/__pycache__/idtracking.cpython-312.pyc b/venv/lib/python3.12/site-packages/jinja2/__pycache__/idtracking.cpython-312.pyc new file mode 100644 index 00000000..9112cd15 Binary files /dev/null and b/venv/lib/python3.12/site-packages/jinja2/__pycache__/idtracking.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jinja2/__pycache__/lexer.cpython-312.pyc b/venv/lib/python3.12/site-packages/jinja2/__pycache__/lexer.cpython-312.pyc new file mode 100644 index 00000000..2bb47932 Binary files /dev/null and b/venv/lib/python3.12/site-packages/jinja2/__pycache__/lexer.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jinja2/__pycache__/loaders.cpython-312.pyc b/venv/lib/python3.12/site-packages/jinja2/__pycache__/loaders.cpython-312.pyc new file mode 100644 index 00000000..bbdd01c1 Binary files /dev/null and b/venv/lib/python3.12/site-packages/jinja2/__pycache__/loaders.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jinja2/__pycache__/meta.cpython-312.pyc b/venv/lib/python3.12/site-packages/jinja2/__pycache__/meta.cpython-312.pyc new file mode 100644 index 00000000..a2791b06 Binary files /dev/null and b/venv/lib/python3.12/site-packages/jinja2/__pycache__/meta.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jinja2/__pycache__/nativetypes.cpython-312.pyc b/venv/lib/python3.12/site-packages/jinja2/__pycache__/nativetypes.cpython-312.pyc new file mode 100644 index 00000000..33c0714f Binary files /dev/null and b/venv/lib/python3.12/site-packages/jinja2/__pycache__/nativetypes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jinja2/__pycache__/nodes.cpython-312.pyc b/venv/lib/python3.12/site-packages/jinja2/__pycache__/nodes.cpython-312.pyc new file mode 100644 index 00000000..14ee7b43 Binary files /dev/null and b/venv/lib/python3.12/site-packages/jinja2/__pycache__/nodes.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jinja2/__pycache__/optimizer.cpython-312.pyc b/venv/lib/python3.12/site-packages/jinja2/__pycache__/optimizer.cpython-312.pyc new file mode 100644 index 00000000..dd3777e0 Binary files /dev/null and b/venv/lib/python3.12/site-packages/jinja2/__pycache__/optimizer.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jinja2/__pycache__/parser.cpython-312.pyc b/venv/lib/python3.12/site-packages/jinja2/__pycache__/parser.cpython-312.pyc new file mode 100644 index 00000000..39001b00 Binary files /dev/null and b/venv/lib/python3.12/site-packages/jinja2/__pycache__/parser.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jinja2/__pycache__/runtime.cpython-312.pyc b/venv/lib/python3.12/site-packages/jinja2/__pycache__/runtime.cpython-312.pyc new file mode 100644 index 00000000..53c2eb3c Binary files /dev/null and b/venv/lib/python3.12/site-packages/jinja2/__pycache__/runtime.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jinja2/__pycache__/sandbox.cpython-312.pyc b/venv/lib/python3.12/site-packages/jinja2/__pycache__/sandbox.cpython-312.pyc new file mode 100644 index 00000000..cfe4eb25 Binary files /dev/null and b/venv/lib/python3.12/site-packages/jinja2/__pycache__/sandbox.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jinja2/__pycache__/tests.cpython-312.pyc b/venv/lib/python3.12/site-packages/jinja2/__pycache__/tests.cpython-312.pyc new file mode 100644 index 00000000..412443c4 Binary files /dev/null and b/venv/lib/python3.12/site-packages/jinja2/__pycache__/tests.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jinja2/__pycache__/utils.cpython-312.pyc b/venv/lib/python3.12/site-packages/jinja2/__pycache__/utils.cpython-312.pyc new file mode 100644 index 00000000..f99729fa Binary files /dev/null and b/venv/lib/python3.12/site-packages/jinja2/__pycache__/utils.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jinja2/__pycache__/visitor.cpython-312.pyc b/venv/lib/python3.12/site-packages/jinja2/__pycache__/visitor.cpython-312.pyc new file mode 100644 index 00000000..241a67ae Binary files /dev/null and b/venv/lib/python3.12/site-packages/jinja2/__pycache__/visitor.cpython-312.pyc differ diff --git a/venv/lib/python3.12/site-packages/jinja2/_identifier.py b/venv/lib/python3.12/site-packages/jinja2/_identifier.py new file mode 100644 index 00000000..928c1503 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jinja2/_identifier.py @@ -0,0 +1,6 @@ +import re + +# generated by scripts/generate_identifier_pattern.py +pattern = re.compile( + r"[\w·̀-ͯ·҃-֑҇-ׇֽֿׁׂׅׄؐ-ًؚ-ٰٟۖ-ۜ۟-۪ۤۧۨ-ܑۭܰ-݊ަ-ް߫-߽߳ࠖ-࠙ࠛ-ࠣࠥ-ࠧࠩ-࡙࠭-࡛࣓-ࣣ࣡-ःऺ-़ा-ॏ॑-ॗॢॣঁ-ঃ়া-ৄেৈো-্ৗৢৣ৾ਁ-ਃ਼ਾ-ੂੇੈੋ-੍ੑੰੱੵઁ-ઃ઼ા-ૅે-ૉો-્ૢૣૺ-૿ଁ-ଃ଼ା-ୄେୈୋ-୍ୖୗୢୣஂா-ூெ-ைொ-்ௗఀ-ఄా-ౄె-ైొ-్ౕౖౢౣಁ-ಃ಼ಾ-ೄೆ-ೈೊ-್ೕೖೢೣഀ-ഃ഻഼ാ-ൄെ-ൈൊ-്ൗൢൣංඃ්ා-ුූෘ-ෟෲෳัิ-ฺ็-๎ັິ-ູົຼ່-ໍ༹༘༙༵༷༾༿ཱ-྄྆྇ྍ-ྗྙ-ྼ࿆ါ-ှၖ-ၙၞ-ၠၢ-ၤၧ-ၭၱ-ၴႂ-ႍႏႚ-ႝ፝-፟ᜒ-᜔ᜲ-᜴ᝒᝓᝲᝳ឴-៓៝᠋-᠍ᢅᢆᢩᤠ-ᤫᤰ-᤻ᨗ-ᨛᩕ-ᩞ᩠-᩿᩼᪰-᪽ᬀ-ᬄ᬴-᭄᭫-᭳ᮀ-ᮂᮡ-ᮭ᯦-᯳ᰤ-᰷᳐-᳔᳒-᳨᳭ᳲ-᳴᳷-᳹᷀-᷹᷻-᷿‿⁀⁔⃐-⃥⃜⃡-⃰℘℮⳯-⵿⳱ⷠ-〪ⷿ-゙゚〯꙯ꙴ-꙽ꚞꚟ꛰꛱ꠂ꠆ꠋꠣ-ꠧꢀꢁꢴ-ꣅ꣠-꣱ꣿꤦ-꤭ꥇ-꥓ꦀ-ꦃ꦳-꧀ꧥꨩ-ꨶꩃꩌꩍꩻ-ꩽꪰꪲ-ꪴꪷꪸꪾ꪿꫁ꫫ-ꫯꫵ꫶ꯣ-ꯪ꯬꯭ﬞ︀-️︠-︯︳︴﹍-﹏_𐇽𐋠𐍶-𐍺𐨁-𐨃𐨅𐨆𐨌-𐨏𐨸-𐨿𐨺𐫦𐫥𐴤-𐽆𐴧-𐽐𑀀-𑀂𑀸-𑁆𑁿-𑂂𑂰-𑂺𑄀-𑄂𑄧-𑄴𑅅𑅆𑅳𑆀-𑆂𑆳-𑇀𑇉-𑇌𑈬-𑈷𑈾𑋟-𑋪𑌀-𑌃𑌻𑌼𑌾-𑍄𑍇𑍈𑍋-𑍍𑍗𑍢𑍣𑍦-𑍬𑍰-𑍴𑐵-𑑆𑑞𑒰-𑓃𑖯-𑖵𑖸-𑗀𑗜𑗝𑘰-𑙀𑚫-𑚷𑜝-𑜫𑠬-𑠺𑨁-𑨊𑨳-𑨹𑨻-𑨾𑩇𑩑-𑩛𑪊-𑪙𑰯-𑰶𑰸-𑰿𑲒-𑲧𑲩-𑲶𑴱-𑴶𑴺𑴼𑴽𑴿-𑵅𑵇𑶊-𑶎𑶐𑶑𑶓-𑶗𑻳-𑻶𖫰-𖫴𖬰-𖬶𖽑-𖽾𖾏-𖾒𛲝𛲞𝅥-𝅩𝅭-𝅲𝅻-𝆂𝆅-𝆋𝆪-𝆭𝉂-𝉄𝨀-𝨶𝨻-𝩬𝩵𝪄𝪛-𝪟𝪡-𝪯𞀀-𞀆𞀈-𞀘𞀛-𞀡𞀣𞀤𞀦-𞣐𞀪-𞣖𞥄-𞥊󠄀-󠇯]+" # noqa: B950 +) diff --git a/venv/lib/python3.12/site-packages/jinja2/async_utils.py b/venv/lib/python3.12/site-packages/jinja2/async_utils.py new file mode 100644 index 00000000..f0c14020 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jinja2/async_utils.py @@ -0,0 +1,99 @@ +import inspect +import typing as t +from functools import WRAPPER_ASSIGNMENTS +from functools import wraps + +from .utils import _PassArg +from .utils import pass_eval_context + +if t.TYPE_CHECKING: + import typing_extensions as te + +V = t.TypeVar("V") + + +def async_variant(normal_func): # type: ignore + def decorator(async_func): # type: ignore + pass_arg = _PassArg.from_obj(normal_func) + need_eval_context = pass_arg is None + + if pass_arg is _PassArg.environment: + + def is_async(args: t.Any) -> bool: + return t.cast(bool, args[0].is_async) + + else: + + def is_async(args: t.Any) -> bool: + return t.cast(bool, args[0].environment.is_async) + + # Take the doc and annotations from the sync function, but the + # name from the async function. Pallets-Sphinx-Themes + # build_function_directive expects __wrapped__ to point to the + # sync function. + async_func_attrs = ("__module__", "__name__", "__qualname__") + normal_func_attrs = tuple(set(WRAPPER_ASSIGNMENTS).difference(async_func_attrs)) + + @wraps(normal_func, assigned=normal_func_attrs) + @wraps(async_func, assigned=async_func_attrs, updated=()) + def wrapper(*args, **kwargs): # type: ignore + b = is_async(args) + + if need_eval_context: + args = args[1:] + + if b: + return async_func(*args, **kwargs) + + return normal_func(*args, **kwargs) + + if need_eval_context: + wrapper = pass_eval_context(wrapper) + + wrapper.jinja_async_variant = True # type: ignore[attr-defined] + return wrapper + + return decorator + + +_common_primitives = {int, float, bool, str, list, dict, tuple, type(None)} + + +async def auto_await(value: t.Union[t.Awaitable["V"], "V"]) -> "V": + # Avoid a costly call to isawaitable + if type(value) in _common_primitives: + return t.cast("V", value) + + if inspect.isawaitable(value): + return await t.cast("t.Awaitable[V]", value) + + return value + + +class _IteratorToAsyncIterator(t.Generic[V]): + def __init__(self, iterator: "t.Iterator[V]"): + self._iterator = iterator + + def __aiter__(self) -> "te.Self": + return self + + async def __anext__(self) -> V: + try: + return next(self._iterator) + except StopIteration as e: + raise StopAsyncIteration(e.value) from e + + +def auto_aiter( + iterable: "t.Union[t.AsyncIterable[V], t.Iterable[V]]", +) -> "t.AsyncIterator[V]": + if hasattr(iterable, "__aiter__"): + return iterable.__aiter__() + else: + return _IteratorToAsyncIterator(iter(iterable)) + + +async def auto_to_list( + value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]", +) -> t.List["V"]: + return [x async for x in auto_aiter(value)] diff --git a/venv/lib/python3.12/site-packages/jinja2/bccache.py b/venv/lib/python3.12/site-packages/jinja2/bccache.py new file mode 100644 index 00000000..ada8b099 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jinja2/bccache.py @@ -0,0 +1,408 @@ +"""The optional bytecode cache system. This is useful if you have very +complex template situations and the compilation of all those templates +slows down your application too much. + +Situations where this is useful are often forking web applications that +are initialized on the first request. +""" + +import errno +import fnmatch +import marshal +import os +import pickle +import stat +import sys +import tempfile +import typing as t +from hashlib import sha1 +from io import BytesIO +from types import CodeType + +if t.TYPE_CHECKING: + import typing_extensions as te + + from .environment import Environment + + class _MemcachedClient(te.Protocol): + def get(self, key: str) -> bytes: ... + + def set( + self, key: str, value: bytes, timeout: t.Optional[int] = None + ) -> None: ... + + +bc_version = 5 +# Magic bytes to identify Jinja bytecode cache files. Contains the +# Python major and minor version to avoid loading incompatible bytecode +# if a project upgrades its Python version. +bc_magic = ( + b"j2" + + pickle.dumps(bc_version, 2) + + pickle.dumps((sys.version_info[0] << 24) | sys.version_info[1], 2) +) + + +class Bucket: + """Buckets are used to store the bytecode for one template. It's created + and initialized by the bytecode cache and passed to the loading functions. + + The buckets get an internal checksum from the cache assigned and use this + to automatically reject outdated cache material. Individual bytecode + cache subclasses don't have to care about cache invalidation. + """ + + def __init__(self, environment: "Environment", key: str, checksum: str) -> None: + self.environment = environment + self.key = key + self.checksum = checksum + self.reset() + + def reset(self) -> None: + """Resets the bucket (unloads the bytecode).""" + self.code: t.Optional[CodeType] = None + + def load_bytecode(self, f: t.BinaryIO) -> None: + """Loads bytecode from a file or file like object.""" + # make sure the magic header is correct + magic = f.read(len(bc_magic)) + if magic != bc_magic: + self.reset() + return + # the source code of the file changed, we need to reload + checksum = pickle.load(f) + if self.checksum != checksum: + self.reset() + return + # if marshal_load fails then we need to reload + try: + self.code = marshal.load(f) + except (EOFError, ValueError, TypeError): + self.reset() + return + + def write_bytecode(self, f: t.IO[bytes]) -> None: + """Dump the bytecode into the file or file like object passed.""" + if self.code is None: + raise TypeError("can't write empty bucket") + f.write(bc_magic) + pickle.dump(self.checksum, f, 2) + marshal.dump(self.code, f) + + def bytecode_from_string(self, string: bytes) -> None: + """Load bytecode from bytes.""" + self.load_bytecode(BytesIO(string)) + + def bytecode_to_string(self) -> bytes: + """Return the bytecode as bytes.""" + out = BytesIO() + self.write_bytecode(out) + return out.getvalue() + + +class BytecodeCache: + """To implement your own bytecode cache you have to subclass this class + and override :meth:`load_bytecode` and :meth:`dump_bytecode`. Both of + these methods are passed a :class:`~jinja2.bccache.Bucket`. + + A very basic bytecode cache that saves the bytecode on the file system:: + + from os import path + + class MyCache(BytecodeCache): + + def __init__(self, directory): + self.directory = directory + + def load_bytecode(self, bucket): + filename = path.join(self.directory, bucket.key) + if path.exists(filename): + with open(filename, 'rb') as f: + bucket.load_bytecode(f) + + def dump_bytecode(self, bucket): + filename = path.join(self.directory, bucket.key) + with open(filename, 'wb') as f: + bucket.write_bytecode(f) + + A more advanced version of a filesystem based bytecode cache is part of + Jinja. + """ + + def load_bytecode(self, bucket: Bucket) -> None: + """Subclasses have to override this method to load bytecode into a + bucket. If they are not able to find code in the cache for the + bucket, it must not do anything. + """ + raise NotImplementedError() + + def dump_bytecode(self, bucket: Bucket) -> None: + """Subclasses have to override this method to write the bytecode + from a bucket back to the cache. If it unable to do so it must not + fail silently but raise an exception. + """ + raise NotImplementedError() + + def clear(self) -> None: + """Clears the cache. This method is not used by Jinja but should be + implemented to allow applications to clear the bytecode cache used + by a particular environment. + """ + + def get_cache_key( + self, name: str, filename: t.Optional[t.Union[str]] = None + ) -> str: + """Returns the unique hash key for this template name.""" + hash = sha1(name.encode("utf-8")) + + if filename is not None: + hash.update(f"|{filename}".encode()) + + return hash.hexdigest() + + def get_source_checksum(self, source: str) -> str: + """Returns a checksum for the source.""" + return sha1(source.encode("utf-8")).hexdigest() + + def get_bucket( + self, + environment: "Environment", + name: str, + filename: t.Optional[str], + source: str, + ) -> Bucket: + """Return a cache bucket for the given template. All arguments are + mandatory but filename may be `None`. + """ + key = self.get_cache_key(name, filename) + checksum = self.get_source_checksum(source) + bucket = Bucket(environment, key, checksum) + self.load_bytecode(bucket) + return bucket + + def set_bucket(self, bucket: Bucket) -> None: + """Put the bucket into the cache.""" + self.dump_bytecode(bucket) + + +class FileSystemBytecodeCache(BytecodeCache): + """A bytecode cache that stores bytecode on the filesystem. It accepts + two arguments: The directory where the cache items are stored and a + pattern string that is used to build the filename. + + If no directory is specified a default cache directory is selected. On + Windows the user's temp directory is used, on UNIX systems a directory + is created for the user in the system temp directory. + + The pattern can be used to have multiple separate caches operate on the + same directory. The default pattern is ``'__jinja2_%s.cache'``. ``%s`` + is replaced with the cache key. + + >>> bcc = FileSystemBytecodeCache('/tmp/jinja_cache', '%s.cache') + + This bytecode cache supports clearing of the cache using the clear method. + """ + + def __init__( + self, directory: t.Optional[str] = None, pattern: str = "__jinja2_%s.cache" + ) -> None: + if directory is None: + directory = self._get_default_cache_dir() + self.directory = directory + self.pattern = pattern + + def _get_default_cache_dir(self) -> str: + def _unsafe_dir() -> "te.NoReturn": + raise RuntimeError( + "Cannot determine safe temp directory. You " + "need to explicitly provide one." + ) + + tmpdir = tempfile.gettempdir() + + # On windows the temporary directory is used specific unless + # explicitly forced otherwise. We can just use that. + if os.name == "nt": + return tmpdir + if not hasattr(os, "getuid"): + _unsafe_dir() + + dirname = f"_jinja2-cache-{os.getuid()}" + actual_dir = os.path.join(tmpdir, dirname) + + try: + os.mkdir(actual_dir, stat.S_IRWXU) + except OSError as e: + if e.errno != errno.EEXIST: + raise + try: + os.chmod(actual_dir, stat.S_IRWXU) + actual_dir_stat = os.lstat(actual_dir) + if ( + actual_dir_stat.st_uid != os.getuid() + or not stat.S_ISDIR(actual_dir_stat.st_mode) + or stat.S_IMODE(actual_dir_stat.st_mode) != stat.S_IRWXU + ): + _unsafe_dir() + except OSError as e: + if e.errno != errno.EEXIST: + raise + + actual_dir_stat = os.lstat(actual_dir) + if ( + actual_dir_stat.st_uid != os.getuid() + or not stat.S_ISDIR(actual_dir_stat.st_mode) + or stat.S_IMODE(actual_dir_stat.st_mode) != stat.S_IRWXU + ): + _unsafe_dir() + + return actual_dir + + def _get_cache_filename(self, bucket: Bucket) -> str: + return os.path.join(self.directory, self.pattern % (bucket.key,)) + + def load_bytecode(self, bucket: Bucket) -> None: + filename = self._get_cache_filename(bucket) + + # Don't test for existence before opening the file, since the + # file could disappear after the test before the open. + try: + f = open(filename, "rb") + except (FileNotFoundError, IsADirectoryError, PermissionError): + # PermissionError can occur on Windows when an operation is + # in progress, such as calling clear(). + return + + with f: + bucket.load_bytecode(f) + + def dump_bytecode(self, bucket: Bucket) -> None: + # Write to a temporary file, then rename to the real name after + # writing. This avoids another process reading the file before + # it is fully written. + name = self._get_cache_filename(bucket) + f = tempfile.NamedTemporaryFile( + mode="wb", + dir=os.path.dirname(name), + prefix=os.path.basename(name), + suffix=".tmp", + delete=False, + ) + + def remove_silent() -> None: + try: + os.remove(f.name) + except OSError: + # Another process may have called clear(). On Windows, + # another program may be holding the file open. + pass + + try: + with f: + bucket.write_bytecode(f) + except BaseException: + remove_silent() + raise + + try: + os.replace(f.name, name) + except OSError: + # Another process may have called clear(). On Windows, + # another program may be holding the file open. + remove_silent() + except BaseException: + remove_silent() + raise + + def clear(self) -> None: + # imported lazily here because google app-engine doesn't support + # write access on the file system and the function does not exist + # normally. + from os import remove + + files = fnmatch.filter(os.listdir(self.directory), self.pattern % ("*",)) + for filename in files: + try: + remove(os.path.join(self.directory, filename)) + except OSError: + pass + + +class MemcachedBytecodeCache(BytecodeCache): + """This class implements a bytecode cache that uses a memcache cache for + storing the information. It does not enforce a specific memcache library + (tummy's memcache or cmemcache) but will accept any class that provides + the minimal interface required. + + Libraries compatible with this class: + + - `cachelib `_ + - `python-memcached `_ + + (Unfortunately the django cache interface is not compatible because it + does not support storing binary data, only text. You can however pass + the underlying cache client to the bytecode cache which is available + as `django.core.cache.cache._client`.) + + The minimal interface for the client passed to the constructor is this: + + .. class:: MinimalClientInterface + + .. method:: set(key, value[, timeout]) + + Stores the bytecode in the cache. `value` is a string and + `timeout` the timeout of the key. If timeout is not provided + a default timeout or no timeout should be assumed, if it's + provided it's an integer with the number of seconds the cache + item should exist. + + .. method:: get(key) + + Returns the value for the cache key. If the item does not + exist in the cache the return value must be `None`. + + The other arguments to the constructor are the prefix for all keys that + is added before the actual cache key and the timeout for the bytecode in + the cache system. We recommend a high (or no) timeout. + + This bytecode cache does not support clearing of used items in the cache. + The clear method is a no-operation function. + + .. versionadded:: 2.7 + Added support for ignoring memcache errors through the + `ignore_memcache_errors` parameter. + """ + + def __init__( + self, + client: "_MemcachedClient", + prefix: str = "jinja2/bytecode/", + timeout: t.Optional[int] = None, + ignore_memcache_errors: bool = True, + ): + self.client = client + self.prefix = prefix + self.timeout = timeout + self.ignore_memcache_errors = ignore_memcache_errors + + def load_bytecode(self, bucket: Bucket) -> None: + try: + code = self.client.get(self.prefix + bucket.key) + except Exception: + if not self.ignore_memcache_errors: + raise + else: + bucket.bytecode_from_string(code) + + def dump_bytecode(self, bucket: Bucket) -> None: + key = self.prefix + bucket.key + value = bucket.bytecode_to_string() + + try: + if self.timeout is not None: + self.client.set(key, value, self.timeout) + else: + self.client.set(key, value) + except Exception: + if not self.ignore_memcache_errors: + raise diff --git a/venv/lib/python3.12/site-packages/jinja2/compiler.py b/venv/lib/python3.12/site-packages/jinja2/compiler.py new file mode 100644 index 00000000..a4ff6a1b --- /dev/null +++ b/venv/lib/python3.12/site-packages/jinja2/compiler.py @@ -0,0 +1,1998 @@ +"""Compiles nodes from the parser into Python code.""" + +import typing as t +from contextlib import contextmanager +from functools import update_wrapper +from io import StringIO +from itertools import chain +from keyword import iskeyword as is_python_keyword + +from markupsafe import escape +from markupsafe import Markup + +from . import nodes +from .exceptions import TemplateAssertionError +from .idtracking import Symbols +from .idtracking import VAR_LOAD_ALIAS +from .idtracking import VAR_LOAD_PARAMETER +from .idtracking import VAR_LOAD_RESOLVE +from .idtracking import VAR_LOAD_UNDEFINED +from .nodes import EvalContext +from .optimizer import Optimizer +from .utils import _PassArg +from .utils import concat +from .visitor import NodeVisitor + +if t.TYPE_CHECKING: + import typing_extensions as te + + from .environment import Environment + +F = t.TypeVar("F", bound=t.Callable[..., t.Any]) + +operators = { + "eq": "==", + "ne": "!=", + "gt": ">", + "gteq": ">=", + "lt": "<", + "lteq": "<=", + "in": "in", + "notin": "not in", +} + + +def optimizeconst(f: F) -> F: + def new_func( + self: "CodeGenerator", node: nodes.Expr, frame: "Frame", **kwargs: t.Any + ) -> t.Any: + # Only optimize if the frame is not volatile + if self.optimizer is not None and not frame.eval_ctx.volatile: + new_node = self.optimizer.visit(node, frame.eval_ctx) + + if new_node != node: + return self.visit(new_node, frame) + + return f(self, node, frame, **kwargs) + + return update_wrapper(new_func, f) # type: ignore[return-value] + + +def _make_binop(op: str) -> t.Callable[["CodeGenerator", nodes.BinExpr, "Frame"], None]: + @optimizeconst + def visitor(self: "CodeGenerator", node: nodes.BinExpr, frame: Frame) -> None: + if ( + self.environment.sandboxed and op in self.environment.intercepted_binops # type: ignore + ): + self.write(f"environment.call_binop(context, {op!r}, ") + self.visit(node.left, frame) + self.write(", ") + self.visit(node.right, frame) + else: + self.write("(") + self.visit(node.left, frame) + self.write(f" {op} ") + self.visit(node.right, frame) + + self.write(")") + + return visitor + + +def _make_unop( + op: str, +) -> t.Callable[["CodeGenerator", nodes.UnaryExpr, "Frame"], None]: + @optimizeconst + def visitor(self: "CodeGenerator", node: nodes.UnaryExpr, frame: Frame) -> None: + if ( + self.environment.sandboxed and op in self.environment.intercepted_unops # type: ignore + ): + self.write(f"environment.call_unop(context, {op!r}, ") + self.visit(node.node, frame) + else: + self.write("(" + op) + self.visit(node.node, frame) + + self.write(")") + + return visitor + + +def generate( + node: nodes.Template, + environment: "Environment", + name: t.Optional[str], + filename: t.Optional[str], + stream: t.Optional[t.TextIO] = None, + defer_init: bool = False, + optimized: bool = True, +) -> t.Optional[str]: + """Generate the python source for a node tree.""" + if not isinstance(node, nodes.Template): + raise TypeError("Can't compile non template nodes") + + generator = environment.code_generator_class( + environment, name, filename, stream, defer_init, optimized + ) + generator.visit(node) + + if stream is None: + return generator.stream.getvalue() # type: ignore + + return None + + +def has_safe_repr(value: t.Any) -> bool: + """Does the node have a safe representation?""" + if value is None or value is NotImplemented or value is Ellipsis: + return True + + if type(value) in {bool, int, float, complex, range, str, Markup}: + return True + + if type(value) in {tuple, list, set, frozenset}: + return all(has_safe_repr(v) for v in value) + + if type(value) is dict: # noqa E721 + return all(has_safe_repr(k) and has_safe_repr(v) for k, v in value.items()) + + return False + + +def find_undeclared( + nodes: t.Iterable[nodes.Node], names: t.Iterable[str] +) -> t.Set[str]: + """Check if the names passed are accessed undeclared. The return value + is a set of all the undeclared names from the sequence of names found. + """ + visitor = UndeclaredNameVisitor(names) + try: + for node in nodes: + visitor.visit(node) + except VisitorExit: + pass + return visitor.undeclared + + +class MacroRef: + def __init__(self, node: t.Union[nodes.Macro, nodes.CallBlock]) -> None: + self.node = node + self.accesses_caller = False + self.accesses_kwargs = False + self.accesses_varargs = False + + +class Frame: + """Holds compile time information for us.""" + + def __init__( + self, + eval_ctx: EvalContext, + parent: t.Optional["Frame"] = None, + level: t.Optional[int] = None, + ) -> None: + self.eval_ctx = eval_ctx + + # the parent of this frame + self.parent = parent + + if parent is None: + self.symbols = Symbols(level=level) + + # in some dynamic inheritance situations the compiler needs to add + # write tests around output statements. + self.require_output_check = False + + # inside some tags we are using a buffer rather than yield statements. + # this for example affects {% filter %} or {% macro %}. If a frame + # is buffered this variable points to the name of the list used as + # buffer. + self.buffer: t.Optional[str] = None + + # the name of the block we're in, otherwise None. + self.block: t.Optional[str] = None + + else: + self.symbols = Symbols(parent.symbols, level=level) + self.require_output_check = parent.require_output_check + self.buffer = parent.buffer + self.block = parent.block + + # a toplevel frame is the root + soft frames such as if conditions. + self.toplevel = False + + # the root frame is basically just the outermost frame, so no if + # conditions. This information is used to optimize inheritance + # situations. + self.rootlevel = False + + # variables set inside of loops and blocks should not affect outer frames, + # but they still needs to be kept track of as part of the active context. + self.loop_frame = False + self.block_frame = False + + # track whether the frame is being used in an if-statement or conditional + # expression as it determines which errors should be raised during runtime + # or compile time. + self.soft_frame = False + + def copy(self) -> "te.Self": + """Create a copy of the current one.""" + rv = object.__new__(self.__class__) + rv.__dict__.update(self.__dict__) + rv.symbols = self.symbols.copy() + return rv + + def inner(self, isolated: bool = False) -> "Frame": + """Return an inner frame.""" + if isolated: + return Frame(self.eval_ctx, level=self.symbols.level + 1) + return Frame(self.eval_ctx, self) + + def soft(self) -> "te.Self": + """Return a soft frame. A soft frame may not be modified as + standalone thing as it shares the resources with the frame it + was created of, but it's not a rootlevel frame any longer. + + This is only used to implement if-statements and conditional + expressions. + """ + rv = self.copy() + rv.rootlevel = False + rv.soft_frame = True + return rv + + __copy__ = copy + + +class VisitorExit(RuntimeError): + """Exception used by the `UndeclaredNameVisitor` to signal a stop.""" + + +class DependencyFinderVisitor(NodeVisitor): + """A visitor that collects filter and test calls.""" + + def __init__(self) -> None: + self.filters: t.Set[str] = set() + self.tests: t.Set[str] = set() + + def visit_Filter(self, node: nodes.Filter) -> None: + self.generic_visit(node) + self.filters.add(node.name) + + def visit_Test(self, node: nodes.Test) -> None: + self.generic_visit(node) + self.tests.add(node.name) + + def visit_Block(self, node: nodes.Block) -> None: + """Stop visiting at blocks.""" + + +class UndeclaredNameVisitor(NodeVisitor): + """A visitor that checks if a name is accessed without being + declared. This is different from the frame visitor as it will + not stop at closure frames. + """ + + def __init__(self, names: t.Iterable[str]) -> None: + self.names = set(names) + self.undeclared: t.Set[str] = set() + + def visit_Name(self, node: nodes.Name) -> None: + if node.ctx == "load" and node.name in self.names: + self.undeclared.add(node.name) + if self.undeclared == self.names: + raise VisitorExit() + else: + self.names.discard(node.name) + + def visit_Block(self, node: nodes.Block) -> None: + """Stop visiting a blocks.""" + + +class CompilerExit(Exception): + """Raised if the compiler encountered a situation where it just + doesn't make sense to further process the code. Any block that + raises such an exception is not further processed. + """ + + +class CodeGenerator(NodeVisitor): + def __init__( + self, + environment: "Environment", + name: t.Optional[str], + filename: t.Optional[str], + stream: t.Optional[t.TextIO] = None, + defer_init: bool = False, + optimized: bool = True, + ) -> None: + if stream is None: + stream = StringIO() + self.environment = environment + self.name = name + self.filename = filename + self.stream = stream + self.created_block_context = False + self.defer_init = defer_init + self.optimizer: t.Optional[Optimizer] = None + + if optimized: + self.optimizer = Optimizer(environment) + + # aliases for imports + self.import_aliases: t.Dict[str, str] = {} + + # a registry for all blocks. Because blocks are moved out + # into the global python scope they are registered here + self.blocks: t.Dict[str, nodes.Block] = {} + + # the number of extends statements so far + self.extends_so_far = 0 + + # some templates have a rootlevel extends. In this case we + # can safely assume that we're a child template and do some + # more optimizations. + self.has_known_extends = False + + # the current line number + self.code_lineno = 1 + + # registry of all filters and tests (global, not block local) + self.tests: t.Dict[str, str] = {} + self.filters: t.Dict[str, str] = {} + + # the debug information + self.debug_info: t.List[t.Tuple[int, int]] = [] + self._write_debug_info: t.Optional[int] = None + + # the number of new lines before the next write() + self._new_lines = 0 + + # the line number of the last written statement + self._last_line = 0 + + # true if nothing was written so far. + self._first_write = True + + # used by the `temporary_identifier` method to get new + # unique, temporary identifier + self._last_identifier = 0 + + # the current indentation + self._indentation = 0 + + # Tracks toplevel assignments + self._assign_stack: t.List[t.Set[str]] = [] + + # Tracks parameter definition blocks + self._param_def_block: t.List[t.Set[str]] = [] + + # Tracks the current context. + self._context_reference_stack = ["context"] + + @property + def optimized(self) -> bool: + return self.optimizer is not None + + # -- Various compilation helpers + + def fail(self, msg: str, lineno: int) -> "te.NoReturn": + """Fail with a :exc:`TemplateAssertionError`.""" + raise TemplateAssertionError(msg, lineno, self.name, self.filename) + + def temporary_identifier(self) -> str: + """Get a new unique identifier.""" + self._last_identifier += 1 + return f"t_{self._last_identifier}" + + def buffer(self, frame: Frame) -> None: + """Enable buffering for the frame from that point onwards.""" + frame.buffer = self.temporary_identifier() + self.writeline(f"{frame.buffer} = []") + + def return_buffer_contents( + self, frame: Frame, force_unescaped: bool = False + ) -> None: + """Return the buffer contents of the frame.""" + if not force_unescaped: + if frame.eval_ctx.volatile: + self.writeline("if context.eval_ctx.autoescape:") + self.indent() + self.writeline(f"return Markup(concat({frame.buffer}))") + self.outdent() + self.writeline("else:") + self.indent() + self.writeline(f"return concat({frame.buffer})") + self.outdent() + return + elif frame.eval_ctx.autoescape: + self.writeline(f"return Markup(concat({frame.buffer}))") + return + self.writeline(f"return concat({frame.buffer})") + + def indent(self) -> None: + """Indent by one.""" + self._indentation += 1 + + def outdent(self, step: int = 1) -> None: + """Outdent by step.""" + self._indentation -= step + + def start_write(self, frame: Frame, node: t.Optional[nodes.Node] = None) -> None: + """Yield or write into the frame buffer.""" + if frame.buffer is None: + self.writeline("yield ", node) + else: + self.writeline(f"{frame.buffer}.append(", node) + + def end_write(self, frame: Frame) -> None: + """End the writing process started by `start_write`.""" + if frame.buffer is not None: + self.write(")") + + def simple_write( + self, s: str, frame: Frame, node: t.Optional[nodes.Node] = None + ) -> None: + """Simple shortcut for start_write + write + end_write.""" + self.start_write(frame, node) + self.write(s) + self.end_write(frame) + + def blockvisit(self, nodes: t.Iterable[nodes.Node], frame: Frame) -> None: + """Visit a list of nodes as block in a frame. If the current frame + is no buffer a dummy ``if 0: yield None`` is written automatically. + """ + try: + self.writeline("pass") + for node in nodes: + self.visit(node, frame) + except CompilerExit: + pass + + def write(self, x: str) -> None: + """Write a string into the output stream.""" + if self._new_lines: + if not self._first_write: + self.stream.write("\n" * self._new_lines) + self.code_lineno += self._new_lines + if self._write_debug_info is not None: + self.debug_info.append((self._write_debug_info, self.code_lineno)) + self._write_debug_info = None + self._first_write = False + self.stream.write(" " * self._indentation) + self._new_lines = 0 + self.stream.write(x) + + def writeline( + self, x: str, node: t.Optional[nodes.Node] = None, extra: int = 0 + ) -> None: + """Combination of newline and write.""" + self.newline(node, extra) + self.write(x) + + def newline(self, node: t.Optional[nodes.Node] = None, extra: int = 0) -> None: + """Add one or more newlines before the next write.""" + self._new_lines = max(self._new_lines, 1 + extra) + if node is not None and node.lineno != self._last_line: + self._write_debug_info = node.lineno + self._last_line = node.lineno + + def signature( + self, + node: t.Union[nodes.Call, nodes.Filter, nodes.Test], + frame: Frame, + extra_kwargs: t.Optional[t.Mapping[str, t.Any]] = None, + ) -> None: + """Writes a function call to the stream for the current node. + A leading comma is added automatically. The extra keyword + arguments may not include python keywords otherwise a syntax + error could occur. The extra keyword arguments should be given + as python dict. + """ + # if any of the given keyword arguments is a python keyword + # we have to make sure that no invalid call is created. + kwarg_workaround = any( + is_python_keyword(t.cast(str, k)) + for k in chain((x.key for x in node.kwargs), extra_kwargs or ()) + ) + + for arg in node.args: + self.write(", ") + self.visit(arg, frame) + + if not kwarg_workaround: + for kwarg in node.kwargs: + self.write(", ") + self.visit(kwarg, frame) + if extra_kwargs is not None: + for key, value in extra_kwargs.items(): + self.write(f", {key}={value}") + if node.dyn_args: + self.write(", *") + self.visit(node.dyn_args, frame) + + if kwarg_workaround: + if node.dyn_kwargs is not None: + self.write(", **dict({") + else: + self.write(", **{") + for kwarg in node.kwargs: + self.write(f"{kwarg.key!r}: ") + self.visit(kwarg.value, frame) + self.write(", ") + if extra_kwargs is not None: + for key, value in extra_kwargs.items(): + self.write(f"{key!r}: {value}, ") + if node.dyn_kwargs is not None: + self.write("}, **") + self.visit(node.dyn_kwargs, frame) + self.write(")") + else: + self.write("}") + + elif node.dyn_kwargs is not None: + self.write(", **") + self.visit(node.dyn_kwargs, frame) + + def pull_dependencies(self, nodes: t.Iterable[nodes.Node]) -> None: + """Find all filter and test names used in the template and + assign them to variables in the compiled namespace. Checking + that the names are registered with the environment is done when + compiling the Filter and Test nodes. If the node is in an If or + CondExpr node, the check is done at runtime instead. + + .. versionchanged:: 3.0 + Filters and tests in If and CondExpr nodes are checked at + runtime instead of compile time. + """ + visitor = DependencyFinderVisitor() + + for node in nodes: + visitor.visit(node) + + for id_map, names, dependency in ( + (self.filters, visitor.filters, "filters"), + ( + self.tests, + visitor.tests, + "tests", + ), + ): + for name in sorted(names): + if name not in id_map: + id_map[name] = self.temporary_identifier() + + # add check during runtime that dependencies used inside of executed + # blocks are defined, as this step may be skipped during compile time + self.writeline("try:") + self.indent() + self.writeline(f"{id_map[name]} = environment.{dependency}[{name!r}]") + self.outdent() + self.writeline("except KeyError:") + self.indent() + self.writeline("@internalcode") + self.writeline(f"def {id_map[name]}(*unused):") + self.indent() + self.writeline( + f'raise TemplateRuntimeError("No {dependency[:-1]}' + f' named {name!r} found.")' + ) + self.outdent() + self.outdent() + + def enter_frame(self, frame: Frame) -> None: + undefs = [] + for target, (action, param) in frame.symbols.loads.items(): + if action == VAR_LOAD_PARAMETER: + pass + elif action == VAR_LOAD_RESOLVE: + self.writeline(f"{target} = {self.get_resolve_func()}({param!r})") + elif action == VAR_LOAD_ALIAS: + self.writeline(f"{target} = {param}") + elif action == VAR_LOAD_UNDEFINED: + undefs.append(target) + else: + raise NotImplementedError("unknown load instruction") + if undefs: + self.writeline(f"{' = '.join(undefs)} = missing") + + def leave_frame(self, frame: Frame, with_python_scope: bool = False) -> None: + if not with_python_scope: + undefs = [] + for target in frame.symbols.loads: + undefs.append(target) + if undefs: + self.writeline(f"{' = '.join(undefs)} = missing") + + def choose_async(self, async_value: str = "async ", sync_value: str = "") -> str: + return async_value if self.environment.is_async else sync_value + + def func(self, name: str) -> str: + return f"{self.choose_async()}def {name}" + + def macro_body( + self, node: t.Union[nodes.Macro, nodes.CallBlock], frame: Frame + ) -> t.Tuple[Frame, MacroRef]: + """Dump the function def of a macro or call block.""" + frame = frame.inner() + frame.symbols.analyze_node(node) + macro_ref = MacroRef(node) + + explicit_caller = None + skip_special_params = set() + args = [] + + for idx, arg in enumerate(node.args): + if arg.name == "caller": + explicit_caller = idx + if arg.name in ("kwargs", "varargs"): + skip_special_params.add(arg.name) + args.append(frame.symbols.ref(arg.name)) + + undeclared = find_undeclared(node.body, ("caller", "kwargs", "varargs")) + + if "caller" in undeclared: + # In older Jinja versions there was a bug that allowed caller + # to retain the special behavior even if it was mentioned in + # the argument list. However thankfully this was only really + # working if it was the last argument. So we are explicitly + # checking this now and error out if it is anywhere else in + # the argument list. + if explicit_caller is not None: + try: + node.defaults[explicit_caller - len(node.args)] + except IndexError: + self.fail( + "When defining macros or call blocks the " + 'special "caller" argument must be omitted ' + "or be given a default.", + node.lineno, + ) + else: + args.append(frame.symbols.declare_parameter("caller")) + macro_ref.accesses_caller = True + if "kwargs" in undeclared and "kwargs" not in skip_special_params: + args.append(frame.symbols.declare_parameter("kwargs")) + macro_ref.accesses_kwargs = True + if "varargs" in undeclared and "varargs" not in skip_special_params: + args.append(frame.symbols.declare_parameter("varargs")) + macro_ref.accesses_varargs = True + + # macros are delayed, they never require output checks + frame.require_output_check = False + frame.symbols.analyze_node(node) + self.writeline(f"{self.func('macro')}({', '.join(args)}):", node) + self.indent() + + self.buffer(frame) + self.enter_frame(frame) + + self.push_parameter_definitions(frame) + for idx, arg in enumerate(node.args): + ref = frame.symbols.ref(arg.name) + self.writeline(f"if {ref} is missing:") + self.indent() + try: + default = node.defaults[idx - len(node.args)] + except IndexError: + self.writeline( + f'{ref} = undefined("parameter {arg.name!r} was not provided",' + f" name={arg.name!r})" + ) + else: + self.writeline(f"{ref} = ") + self.visit(default, frame) + self.mark_parameter_stored(ref) + self.outdent() + self.pop_parameter_definitions() + + self.blockvisit(node.body, frame) + self.return_buffer_contents(frame, force_unescaped=True) + self.leave_frame(frame, with_python_scope=True) + self.outdent() + + return frame, macro_ref + + def macro_def(self, macro_ref: MacroRef, frame: Frame) -> None: + """Dump the macro definition for the def created by macro_body.""" + arg_tuple = ", ".join(repr(x.name) for x in macro_ref.node.args) + name = getattr(macro_ref.node, "name", None) + if len(macro_ref.node.args) == 1: + arg_tuple += "," + self.write( + f"Macro(environment, macro, {name!r}, ({arg_tuple})," + f" {macro_ref.accesses_kwargs!r}, {macro_ref.accesses_varargs!r}," + f" {macro_ref.accesses_caller!r}, context.eval_ctx.autoescape)" + ) + + def position(self, node: nodes.Node) -> str: + """Return a human readable position for the node.""" + rv = f"line {node.lineno}" + if self.name is not None: + rv = f"{rv} in {self.name!r}" + return rv + + def dump_local_context(self, frame: Frame) -> str: + items_kv = ", ".join( + f"{name!r}: {target}" + for name, target in frame.symbols.dump_stores().items() + ) + return f"{{{items_kv}}}" + + def write_commons(self) -> None: + """Writes a common preamble that is used by root and block functions. + Primarily this sets up common local helpers and enforces a generator + through a dead branch. + """ + self.writeline("resolve = context.resolve_or_missing") + self.writeline("undefined = environment.undefined") + self.writeline("concat = environment.concat") + # always use the standard Undefined class for the implicit else of + # conditional expressions + self.writeline("cond_expr_undefined = Undefined") + self.writeline("if 0: yield None") + + def push_parameter_definitions(self, frame: Frame) -> None: + """Pushes all parameter targets from the given frame into a local + stack that permits tracking of yet to be assigned parameters. In + particular this enables the optimization from `visit_Name` to skip + undefined expressions for parameters in macros as macros can reference + otherwise unbound parameters. + """ + self._param_def_block.append(frame.symbols.dump_param_targets()) + + def pop_parameter_definitions(self) -> None: + """Pops the current parameter definitions set.""" + self._param_def_block.pop() + + def mark_parameter_stored(self, target: str) -> None: + """Marks a parameter in the current parameter definitions as stored. + This will skip the enforced undefined checks. + """ + if self._param_def_block: + self._param_def_block[-1].discard(target) + + def push_context_reference(self, target: str) -> None: + self._context_reference_stack.append(target) + + def pop_context_reference(self) -> None: + self._context_reference_stack.pop() + + def get_context_ref(self) -> str: + return self._context_reference_stack[-1] + + def get_resolve_func(self) -> str: + target = self._context_reference_stack[-1] + if target == "context": + return "resolve" + return f"{target}.resolve" + + def derive_context(self, frame: Frame) -> str: + return f"{self.get_context_ref()}.derived({self.dump_local_context(frame)})" + + def parameter_is_undeclared(self, target: str) -> bool: + """Checks if a given target is an undeclared parameter.""" + if not self._param_def_block: + return False + return target in self._param_def_block[-1] + + def push_assign_tracking(self) -> None: + """Pushes a new layer for assignment tracking.""" + self._assign_stack.append(set()) + + def pop_assign_tracking(self, frame: Frame) -> None: + """Pops the topmost level for assignment tracking and updates the + context variables if necessary. + """ + vars = self._assign_stack.pop() + if ( + not frame.block_frame + and not frame.loop_frame + and not frame.toplevel + or not vars + ): + return + public_names = [x for x in vars if x[:1] != "_"] + if len(vars) == 1: + name = next(iter(vars)) + ref = frame.symbols.ref(name) + if frame.loop_frame: + self.writeline(f"_loop_vars[{name!r}] = {ref}") + return + if frame.block_frame: + self.writeline(f"_block_vars[{name!r}] = {ref}") + return + self.writeline(f"context.vars[{name!r}] = {ref}") + else: + if frame.loop_frame: + self.writeline("_loop_vars.update({") + elif frame.block_frame: + self.writeline("_block_vars.update({") + else: + self.writeline("context.vars.update({") + for idx, name in enumerate(sorted(vars)): + if idx: + self.write(", ") + ref = frame.symbols.ref(name) + self.write(f"{name!r}: {ref}") + self.write("})") + if not frame.block_frame and not frame.loop_frame and public_names: + if len(public_names) == 1: + self.writeline(f"context.exported_vars.add({public_names[0]!r})") + else: + names_str = ", ".join(map(repr, sorted(public_names))) + self.writeline(f"context.exported_vars.update(({names_str}))") + + # -- Statement Visitors + + def visit_Template( + self, node: nodes.Template, frame: t.Optional[Frame] = None + ) -> None: + assert frame is None, "no root frame allowed" + eval_ctx = EvalContext(self.environment, self.name) + + from .runtime import async_exported + from .runtime import exported + + if self.environment.is_async: + exported_names = sorted(exported + async_exported) + else: + exported_names = sorted(exported) + + self.writeline("from jinja2.runtime import " + ", ".join(exported_names)) + + # if we want a deferred initialization we cannot move the + # environment into a local name + envenv = "" if self.defer_init else ", environment=environment" + + # do we have an extends tag at all? If not, we can save some + # overhead by just not processing any inheritance code. + have_extends = node.find(nodes.Extends) is not None + + # find all blocks + for block in node.find_all(nodes.Block): + if block.name in self.blocks: + self.fail(f"block {block.name!r} defined twice", block.lineno) + self.blocks[block.name] = block + + # find all imports and import them + for import_ in node.find_all(nodes.ImportedName): + if import_.importname not in self.import_aliases: + imp = import_.importname + self.import_aliases[imp] = alias = self.temporary_identifier() + if "." in imp: + module, obj = imp.rsplit(".", 1) + self.writeline(f"from {module} import {obj} as {alias}") + else: + self.writeline(f"import {imp} as {alias}") + + # add the load name + self.writeline(f"name = {self.name!r}") + + # generate the root render function. + self.writeline( + f"{self.func('root')}(context, missing=missing{envenv}):", extra=1 + ) + self.indent() + self.write_commons() + + # process the root + frame = Frame(eval_ctx) + if "self" in find_undeclared(node.body, ("self",)): + ref = frame.symbols.declare_parameter("self") + self.writeline(f"{ref} = TemplateReference(context)") + frame.symbols.analyze_node(node) + frame.toplevel = frame.rootlevel = True + frame.require_output_check = have_extends and not self.has_known_extends + if have_extends: + self.writeline("parent_template = None") + self.enter_frame(frame) + self.pull_dependencies(node.body) + self.blockvisit(node.body, frame) + self.leave_frame(frame, with_python_scope=True) + self.outdent() + + # make sure that the parent root is called. + if have_extends: + if not self.has_known_extends: + self.indent() + self.writeline("if parent_template is not None:") + self.indent() + if not self.environment.is_async: + self.writeline("yield from parent_template.root_render_func(context)") + else: + self.writeline("agen = parent_template.root_render_func(context)") + self.writeline("try:") + self.indent() + self.writeline("async for event in agen:") + self.indent() + self.writeline("yield event") + self.outdent() + self.outdent() + self.writeline("finally: await agen.aclose()") + self.outdent(1 + (not self.has_known_extends)) + + # at this point we now have the blocks collected and can visit them too. + for name, block in self.blocks.items(): + self.writeline( + f"{self.func('block_' + name)}(context, missing=missing{envenv}):", + block, + 1, + ) + self.indent() + self.write_commons() + # It's important that we do not make this frame a child of the + # toplevel template. This would cause a variety of + # interesting issues with identifier tracking. + block_frame = Frame(eval_ctx) + block_frame.block_frame = True + undeclared = find_undeclared(block.body, ("self", "super")) + if "self" in undeclared: + ref = block_frame.symbols.declare_parameter("self") + self.writeline(f"{ref} = TemplateReference(context)") + if "super" in undeclared: + ref = block_frame.symbols.declare_parameter("super") + self.writeline(f"{ref} = context.super({name!r}, block_{name})") + block_frame.symbols.analyze_node(block) + block_frame.block = name + self.writeline("_block_vars = {}") + self.enter_frame(block_frame) + self.pull_dependencies(block.body) + self.blockvisit(block.body, block_frame) + self.leave_frame(block_frame, with_python_scope=True) + self.outdent() + + blocks_kv_str = ", ".join(f"{x!r}: block_{x}" for x in self.blocks) + self.writeline(f"blocks = {{{blocks_kv_str}}}", extra=1) + debug_kv_str = "&".join(f"{k}={v}" for k, v in self.debug_info) + self.writeline(f"debug_info = {debug_kv_str!r}") + + def visit_Block(self, node: nodes.Block, frame: Frame) -> None: + """Call a block and register it for the template.""" + level = 0 + if frame.toplevel: + # if we know that we are a child template, there is no need to + # check if we are one + if self.has_known_extends: + return + if self.extends_so_far > 0: + self.writeline("if parent_template is None:") + self.indent() + level += 1 + + if node.scoped: + context = self.derive_context(frame) + else: + context = self.get_context_ref() + + if node.required: + self.writeline(f"if len(context.blocks[{node.name!r}]) <= 1:", node) + self.indent() + self.writeline( + f'raise TemplateRuntimeError("Required block {node.name!r} not found")', + node, + ) + self.outdent() + + if not self.environment.is_async and frame.buffer is None: + self.writeline( + f"yield from context.blocks[{node.name!r}][0]({context})", node + ) + else: + self.writeline(f"gen = context.blocks[{node.name!r}][0]({context})") + self.writeline("try:") + self.indent() + self.writeline( + f"{self.choose_async()}for event in gen:", + node, + ) + self.indent() + self.simple_write("event", frame) + self.outdent() + self.outdent() + self.writeline( + f"finally: {self.choose_async('await gen.aclose()', 'gen.close()')}" + ) + + self.outdent(level) + + def visit_Extends(self, node: nodes.Extends, frame: Frame) -> None: + """Calls the extender.""" + if not frame.toplevel: + self.fail("cannot use extend from a non top-level scope", node.lineno) + + # if the number of extends statements in general is zero so + # far, we don't have to add a check if something extended + # the template before this one. + if self.extends_so_far > 0: + # if we have a known extends we just add a template runtime + # error into the generated code. We could catch that at compile + # time too, but i welcome it not to confuse users by throwing the + # same error at different times just "because we can". + if not self.has_known_extends: + self.writeline("if parent_template is not None:") + self.indent() + self.writeline('raise TemplateRuntimeError("extended multiple times")') + + # if we have a known extends already we don't need that code here + # as we know that the template execution will end here. + if self.has_known_extends: + raise CompilerExit() + else: + self.outdent() + + self.writeline("parent_template = environment.get_template(", node) + self.visit(node.template, frame) + self.write(f", {self.name!r})") + self.writeline("for name, parent_block in parent_template.blocks.items():") + self.indent() + self.writeline("context.blocks.setdefault(name, []).append(parent_block)") + self.outdent() + + # if this extends statement was in the root level we can take + # advantage of that information and simplify the generated code + # in the top level from this point onwards + if frame.rootlevel: + self.has_known_extends = True + + # and now we have one more + self.extends_so_far += 1 + + def visit_Include(self, node: nodes.Include, frame: Frame) -> None: + """Handles includes.""" + if node.ignore_missing: + self.writeline("try:") + self.indent() + + func_name = "get_or_select_template" + if isinstance(node.template, nodes.Const): + if isinstance(node.template.value, str): + func_name = "get_template" + elif isinstance(node.template.value, (tuple, list)): + func_name = "select_template" + elif isinstance(node.template, (nodes.Tuple, nodes.List)): + func_name = "select_template" + + self.writeline(f"template = environment.{func_name}(", node) + self.visit(node.template, frame) + self.write(f", {self.name!r})") + if node.ignore_missing: + self.outdent() + self.writeline("except TemplateNotFound:") + self.indent() + self.writeline("pass") + self.outdent() + self.writeline("else:") + self.indent() + + def loop_body() -> None: + self.indent() + self.simple_write("event", frame) + self.outdent() + + if node.with_context: + self.writeline( + f"gen = template.root_render_func(" + "template.new_context(context.get_all(), True," + f" {self.dump_local_context(frame)}))" + ) + self.writeline("try:") + self.indent() + self.writeline(f"{self.choose_async()}for event in gen:") + loop_body() + self.outdent() + self.writeline( + f"finally: {self.choose_async('await gen.aclose()', 'gen.close()')}" + ) + elif self.environment.is_async: + self.writeline( + "for event in (await template._get_default_module_async())" + "._body_stream:" + ) + loop_body() + else: + self.writeline("yield from template._get_default_module()._body_stream") + + if node.ignore_missing: + self.outdent() + + def _import_common( + self, node: t.Union[nodes.Import, nodes.FromImport], frame: Frame + ) -> None: + self.write(f"{self.choose_async('await ')}environment.get_template(") + self.visit(node.template, frame) + self.write(f", {self.name!r}).") + + if node.with_context: + f_name = f"make_module{self.choose_async('_async')}" + self.write( + f"{f_name}(context.get_all(), True, {self.dump_local_context(frame)})" + ) + else: + self.write(f"_get_default_module{self.choose_async('_async')}(context)") + + def visit_Import(self, node: nodes.Import, frame: Frame) -> None: + """Visit regular imports.""" + self.writeline(f"{frame.symbols.ref(node.target)} = ", node) + if frame.toplevel: + self.write(f"context.vars[{node.target!r}] = ") + + self._import_common(node, frame) + + if frame.toplevel and not node.target.startswith("_"): + self.writeline(f"context.exported_vars.discard({node.target!r})") + + def visit_FromImport(self, node: nodes.FromImport, frame: Frame) -> None: + """Visit named imports.""" + self.newline(node) + self.write("included_template = ") + self._import_common(node, frame) + var_names = [] + discarded_names = [] + for name in node.names: + if isinstance(name, tuple): + name, alias = name + else: + alias = name + self.writeline( + f"{frame.symbols.ref(alias)} =" + f" getattr(included_template, {name!r}, missing)" + ) + self.writeline(f"if {frame.symbols.ref(alias)} is missing:") + self.indent() + # The position will contain the template name, and will be formatted + # into a string that will be compiled into an f-string. Curly braces + # in the name must be replaced with escapes so that they will not be + # executed as part of the f-string. + position = self.position(node).replace("{", "{{").replace("}", "}}") + message = ( + "the template {included_template.__name__!r}" + f" (imported on {position})" + f" does not export the requested name {name!r}" + ) + self.writeline( + f"{frame.symbols.ref(alias)} = undefined(f{message!r}, name={name!r})" + ) + self.outdent() + if frame.toplevel: + var_names.append(alias) + if not alias.startswith("_"): + discarded_names.append(alias) + + if var_names: + if len(var_names) == 1: + name = var_names[0] + self.writeline(f"context.vars[{name!r}] = {frame.symbols.ref(name)}") + else: + names_kv = ", ".join( + f"{name!r}: {frame.symbols.ref(name)}" for name in var_names + ) + self.writeline(f"context.vars.update({{{names_kv}}})") + if discarded_names: + if len(discarded_names) == 1: + self.writeline(f"context.exported_vars.discard({discarded_names[0]!r})") + else: + names_str = ", ".join(map(repr, discarded_names)) + self.writeline( + f"context.exported_vars.difference_update(({names_str}))" + ) + + def visit_For(self, node: nodes.For, frame: Frame) -> None: + loop_frame = frame.inner() + loop_frame.loop_frame = True + test_frame = frame.inner() + else_frame = frame.inner() + + # try to figure out if we have an extended loop. An extended loop + # is necessary if the loop is in recursive mode if the special loop + # variable is accessed in the body if the body is a scoped block. + extended_loop = ( + node.recursive + or "loop" + in find_undeclared(node.iter_child_nodes(only=("body",)), ("loop",)) + or any(block.scoped for block in node.find_all(nodes.Block)) + ) + + loop_ref = None + if extended_loop: + loop_ref = loop_frame.symbols.declare_parameter("loop") + + loop_frame.symbols.analyze_node(node, for_branch="body") + if node.else_: + else_frame.symbols.analyze_node(node, for_branch="else") + + if node.test: + loop_filter_func = self.temporary_identifier() + test_frame.symbols.analyze_node(node, for_branch="test") + self.writeline(f"{self.func(loop_filter_func)}(fiter):", node.test) + self.indent() + self.enter_frame(test_frame) + self.writeline(self.choose_async("async for ", "for ")) + self.visit(node.target, loop_frame) + self.write(" in ") + self.write(self.choose_async("auto_aiter(fiter)", "fiter")) + self.write(":") + self.indent() + self.writeline("if ", node.test) + self.visit(node.test, test_frame) + self.write(":") + self.indent() + self.writeline("yield ") + self.visit(node.target, loop_frame) + self.outdent(3) + self.leave_frame(test_frame, with_python_scope=True) + + # if we don't have an recursive loop we have to find the shadowed + # variables at that point. Because loops can be nested but the loop + # variable is a special one we have to enforce aliasing for it. + if node.recursive: + self.writeline( + f"{self.func('loop')}(reciter, loop_render_func, depth=0):", node + ) + self.indent() + self.buffer(loop_frame) + + # Use the same buffer for the else frame + else_frame.buffer = loop_frame.buffer + + # make sure the loop variable is a special one and raise a template + # assertion error if a loop tries to write to loop + if extended_loop: + self.writeline(f"{loop_ref} = missing") + + for name in node.find_all(nodes.Name): + if name.ctx == "store" and name.name == "loop": + self.fail( + "Can't assign to special loop variable in for-loop target", + name.lineno, + ) + + if node.else_: + iteration_indicator = self.temporary_identifier() + self.writeline(f"{iteration_indicator} = 1") + + self.writeline(self.choose_async("async for ", "for "), node) + self.visit(node.target, loop_frame) + if extended_loop: + self.write(f", {loop_ref} in {self.choose_async('Async')}LoopContext(") + else: + self.write(" in ") + + if node.test: + self.write(f"{loop_filter_func}(") + if node.recursive: + self.write("reciter") + else: + if self.environment.is_async and not extended_loop: + self.write("auto_aiter(") + self.visit(node.iter, frame) + if self.environment.is_async and not extended_loop: + self.write(")") + if node.test: + self.write(")") + + if node.recursive: + self.write(", undefined, loop_render_func, depth):") + else: + self.write(", undefined):" if extended_loop else ":") + + self.indent() + self.enter_frame(loop_frame) + + self.writeline("_loop_vars = {}") + self.blockvisit(node.body, loop_frame) + if node.else_: + self.writeline(f"{iteration_indicator} = 0") + self.outdent() + self.leave_frame( + loop_frame, with_python_scope=node.recursive and not node.else_ + ) + + if node.else_: + self.writeline(f"if {iteration_indicator}:") + self.indent() + self.enter_frame(else_frame) + self.blockvisit(node.else_, else_frame) + self.leave_frame(else_frame) + self.outdent() + + # if the node was recursive we have to return the buffer contents + # and start the iteration code + if node.recursive: + self.return_buffer_contents(loop_frame) + self.outdent() + self.start_write(frame, node) + self.write(f"{self.choose_async('await ')}loop(") + if self.environment.is_async: + self.write("auto_aiter(") + self.visit(node.iter, frame) + if self.environment.is_async: + self.write(")") + self.write(", loop)") + self.end_write(frame) + + # at the end of the iteration, clear any assignments made in the + # loop from the top level + if self._assign_stack: + self._assign_stack[-1].difference_update(loop_frame.symbols.stores) + + def visit_If(self, node: nodes.If, frame: Frame) -> None: + if_frame = frame.soft() + self.writeline("if ", node) + self.visit(node.test, if_frame) + self.write(":") + self.indent() + self.blockvisit(node.body, if_frame) + self.outdent() + for elif_ in node.elif_: + self.writeline("elif ", elif_) + self.visit(elif_.test, if_frame) + self.write(":") + self.indent() + self.blockvisit(elif_.body, if_frame) + self.outdent() + if node.else_: + self.writeline("else:") + self.indent() + self.blockvisit(node.else_, if_frame) + self.outdent() + + def visit_Macro(self, node: nodes.Macro, frame: Frame) -> None: + macro_frame, macro_ref = self.macro_body(node, frame) + self.newline() + if frame.toplevel: + if not node.name.startswith("_"): + self.write(f"context.exported_vars.add({node.name!r})") + self.writeline(f"context.vars[{node.name!r}] = ") + self.write(f"{frame.symbols.ref(node.name)} = ") + self.macro_def(macro_ref, macro_frame) + + def visit_CallBlock(self, node: nodes.CallBlock, frame: Frame) -> None: + call_frame, macro_ref = self.macro_body(node, frame) + self.writeline("caller = ") + self.macro_def(macro_ref, call_frame) + self.start_write(frame, node) + self.visit_Call(node.call, frame, forward_caller=True) + self.end_write(frame) + + def visit_FilterBlock(self, node: nodes.FilterBlock, frame: Frame) -> None: + filter_frame = frame.inner() + filter_frame.symbols.analyze_node(node) + self.enter_frame(filter_frame) + self.buffer(filter_frame) + self.blockvisit(node.body, filter_frame) + self.start_write(frame, node) + self.visit_Filter(node.filter, filter_frame) + self.end_write(frame) + self.leave_frame(filter_frame) + + def visit_With(self, node: nodes.With, frame: Frame) -> None: + with_frame = frame.inner() + with_frame.symbols.analyze_node(node) + self.enter_frame(with_frame) + for target, expr in zip(node.targets, node.values): + self.newline() + self.visit(target, with_frame) + self.write(" = ") + self.visit(expr, frame) + self.blockvisit(node.body, with_frame) + self.leave_frame(with_frame) + + def visit_ExprStmt(self, node: nodes.ExprStmt, frame: Frame) -> None: + self.newline(node) + self.visit(node.node, frame) + + class _FinalizeInfo(t.NamedTuple): + const: t.Optional[t.Callable[..., str]] + src: t.Optional[str] + + @staticmethod + def _default_finalize(value: t.Any) -> t.Any: + """The default finalize function if the environment isn't + configured with one. Or, if the environment has one, this is + called on that function's output for constants. + """ + return str(value) + + _finalize: t.Optional[_FinalizeInfo] = None + + def _make_finalize(self) -> _FinalizeInfo: + """Build the finalize function to be used on constants and at + runtime. Cached so it's only created once for all output nodes. + + Returns a ``namedtuple`` with the following attributes: + + ``const`` + A function to finalize constant data at compile time. + + ``src`` + Source code to output around nodes to be evaluated at + runtime. + """ + if self._finalize is not None: + return self._finalize + + finalize: t.Optional[t.Callable[..., t.Any]] + finalize = default = self._default_finalize + src = None + + if self.environment.finalize: + src = "environment.finalize(" + env_finalize = self.environment.finalize + pass_arg = { + _PassArg.context: "context", + _PassArg.eval_context: "context.eval_ctx", + _PassArg.environment: "environment", + }.get( + _PassArg.from_obj(env_finalize) # type: ignore + ) + finalize = None + + if pass_arg is None: + + def finalize(value: t.Any) -> t.Any: # noqa: F811 + return default(env_finalize(value)) + + else: + src = f"{src}{pass_arg}, " + + if pass_arg == "environment": + + def finalize(value: t.Any) -> t.Any: # noqa: F811 + return default(env_finalize(self.environment, value)) + + self._finalize = self._FinalizeInfo(finalize, src) + return self._finalize + + def _output_const_repr(self, group: t.Iterable[t.Any]) -> str: + """Given a group of constant values converted from ``Output`` + child nodes, produce a string to write to the template module + source. + """ + return repr(concat(group)) + + def _output_child_to_const( + self, node: nodes.Expr, frame: Frame, finalize: _FinalizeInfo + ) -> str: + """Try to optimize a child of an ``Output`` node by trying to + convert it to constant, finalized data at compile time. + + If :exc:`Impossible` is raised, the node is not constant and + will be evaluated at runtime. Any other exception will also be + evaluated at runtime for easier debugging. + """ + const = node.as_const(frame.eval_ctx) + + if frame.eval_ctx.autoescape: + const = escape(const) + + # Template data doesn't go through finalize. + if isinstance(node, nodes.TemplateData): + return str(const) + + return finalize.const(const) # type: ignore + + def _output_child_pre( + self, node: nodes.Expr, frame: Frame, finalize: _FinalizeInfo + ) -> None: + """Output extra source code before visiting a child of an + ``Output`` node. + """ + if frame.eval_ctx.volatile: + self.write("(escape if context.eval_ctx.autoescape else str)(") + elif frame.eval_ctx.autoescape: + self.write("escape(") + else: + self.write("str(") + + if finalize.src is not None: + self.write(finalize.src) + + def _output_child_post( + self, node: nodes.Expr, frame: Frame, finalize: _FinalizeInfo + ) -> None: + """Output extra source code after visiting a child of an + ``Output`` node. + """ + self.write(")") + + if finalize.src is not None: + self.write(")") + + def visit_Output(self, node: nodes.Output, frame: Frame) -> None: + # If an extends is active, don't render outside a block. + if frame.require_output_check: + # A top-level extends is known to exist at compile time. + if self.has_known_extends: + return + + self.writeline("if parent_template is None:") + self.indent() + + finalize = self._make_finalize() + body: t.List[t.Union[t.List[t.Any], nodes.Expr]] = [] + + # Evaluate constants at compile time if possible. Each item in + # body will be either a list of static data or a node to be + # evaluated at runtime. + for child in node.nodes: + try: + if not ( + # If the finalize function requires runtime context, + # constants can't be evaluated at compile time. + finalize.const + # Unless it's basic template data that won't be + # finalized anyway. + or isinstance(child, nodes.TemplateData) + ): + raise nodes.Impossible() + + const = self._output_child_to_const(child, frame, finalize) + except (nodes.Impossible, Exception): + # The node was not constant and needs to be evaluated at + # runtime. Or another error was raised, which is easier + # to debug at runtime. + body.append(child) + continue + + if body and isinstance(body[-1], list): + body[-1].append(const) + else: + body.append([const]) + + if frame.buffer is not None: + if len(body) == 1: + self.writeline(f"{frame.buffer}.append(") + else: + self.writeline(f"{frame.buffer}.extend((") + + self.indent() + + for item in body: + if isinstance(item, list): + # A group of constant data to join and output. + val = self._output_const_repr(item) + + if frame.buffer is None: + self.writeline("yield " + val) + else: + self.writeline(val + ",") + else: + if frame.buffer is None: + self.writeline("yield ", item) + else: + self.newline(item) + + # A node to be evaluated at runtime. + self._output_child_pre(item, frame, finalize) + self.visit(item, frame) + self._output_child_post(item, frame, finalize) + + if frame.buffer is not None: + self.write(",") + + if frame.buffer is not None: + self.outdent() + self.writeline(")" if len(body) == 1 else "))") + + if frame.require_output_check: + self.outdent() + + def visit_Assign(self, node: nodes.Assign, frame: Frame) -> None: + self.push_assign_tracking() + + # ``a.b`` is allowed for assignment, and is parsed as an NSRef. However, + # it is only valid if it references a Namespace object. Emit a check for + # that for each ref here, before assignment code is emitted. This can't + # be done in visit_NSRef as the ref could be in the middle of a tuple. + seen_refs: t.Set[str] = set() + + for nsref in node.find_all(nodes.NSRef): + if nsref.name in seen_refs: + # Only emit the check for each reference once, in case the same + # ref is used multiple times in a tuple, `ns.a, ns.b = c, d`. + continue + + seen_refs.add(nsref.name) + ref = frame.symbols.ref(nsref.name) + self.writeline(f"if not isinstance({ref}, Namespace):") + self.indent() + self.writeline( + "raise TemplateRuntimeError" + '("cannot assign attribute on non-namespace object")' + ) + self.outdent() + + self.newline(node) + self.visit(node.target, frame) + self.write(" = ") + self.visit(node.node, frame) + self.pop_assign_tracking(frame) + + def visit_AssignBlock(self, node: nodes.AssignBlock, frame: Frame) -> None: + self.push_assign_tracking() + block_frame = frame.inner() + # This is a special case. Since a set block always captures we + # will disable output checks. This way one can use set blocks + # toplevel even in extended templates. + block_frame.require_output_check = False + block_frame.symbols.analyze_node(node) + self.enter_frame(block_frame) + self.buffer(block_frame) + self.blockvisit(node.body, block_frame) + self.newline(node) + self.visit(node.target, frame) + self.write(" = (Markup if context.eval_ctx.autoescape else identity)(") + if node.filter is not None: + self.visit_Filter(node.filter, block_frame) + else: + self.write(f"concat({block_frame.buffer})") + self.write(")") + self.pop_assign_tracking(frame) + self.leave_frame(block_frame) + + # -- Expression Visitors + + def visit_Name(self, node: nodes.Name, frame: Frame) -> None: + if node.ctx == "store" and ( + frame.toplevel or frame.loop_frame or frame.block_frame + ): + if self._assign_stack: + self._assign_stack[-1].add(node.name) + ref = frame.symbols.ref(node.name) + + # If we are looking up a variable we might have to deal with the + # case where it's undefined. We can skip that case if the load + # instruction indicates a parameter which are always defined. + if node.ctx == "load": + load = frame.symbols.find_load(ref) + if not ( + load is not None + and load[0] == VAR_LOAD_PARAMETER + and not self.parameter_is_undeclared(ref) + ): + self.write( + f"(undefined(name={node.name!r}) if {ref} is missing else {ref})" + ) + return + + self.write(ref) + + def visit_NSRef(self, node: nodes.NSRef, frame: Frame) -> None: + # NSRef is a dotted assignment target a.b=c, but uses a[b]=c internally. + # visit_Assign emits code to validate that each ref is to a Namespace + # object only. That can't be emitted here as the ref could be in the + # middle of a tuple assignment. + ref = frame.symbols.ref(node.name) + self.writeline(f"{ref}[{node.attr!r}]") + + def visit_Const(self, node: nodes.Const, frame: Frame) -> None: + val = node.as_const(frame.eval_ctx) + if isinstance(val, float): + self.write(str(val)) + else: + self.write(repr(val)) + + def visit_TemplateData(self, node: nodes.TemplateData, frame: Frame) -> None: + try: + self.write(repr(node.as_const(frame.eval_ctx))) + except nodes.Impossible: + self.write( + f"(Markup if context.eval_ctx.autoescape else identity)({node.data!r})" + ) + + def visit_Tuple(self, node: nodes.Tuple, frame: Frame) -> None: + self.write("(") + idx = -1 + for idx, item in enumerate(node.items): + if idx: + self.write(", ") + self.visit(item, frame) + self.write(",)" if idx == 0 else ")") + + def visit_List(self, node: nodes.List, frame: Frame) -> None: + self.write("[") + for idx, item in enumerate(node.items): + if idx: + self.write(", ") + self.visit(item, frame) + self.write("]") + + def visit_Dict(self, node: nodes.Dict, frame: Frame) -> None: + self.write("{") + for idx, item in enumerate(node.items): + if idx: + self.write(", ") + self.visit(item.key, frame) + self.write(": ") + self.visit(item.value, frame) + self.write("}") + + visit_Add = _make_binop("+") + visit_Sub = _make_binop("-") + visit_Mul = _make_binop("*") + visit_Div = _make_binop("/") + visit_FloorDiv = _make_binop("//") + visit_Pow = _make_binop("**") + visit_Mod = _make_binop("%") + visit_And = _make_binop("and") + visit_Or = _make_binop("or") + visit_Pos = _make_unop("+") + visit_Neg = _make_unop("-") + visit_Not = _make_unop("not ") + + @optimizeconst + def visit_Concat(self, node: nodes.Concat, frame: Frame) -> None: + if frame.eval_ctx.volatile: + func_name = "(markup_join if context.eval_ctx.volatile else str_join)" + elif frame.eval_ctx.autoescape: + func_name = "markup_join" + else: + func_name = "str_join" + self.write(f"{func_name}((") + for arg in node.nodes: + self.visit(arg, frame) + self.write(", ") + self.write("))") + + @optimizeconst + def visit_Compare(self, node: nodes.Compare, frame: Frame) -> None: + self.write("(") + self.visit(node.expr, frame) + for op in node.ops: + self.visit(op, frame) + self.write(")") + + def visit_Operand(self, node: nodes.Operand, frame: Frame) -> None: + self.write(f" {operators[node.op]} ") + self.visit(node.expr, frame) + + @optimizeconst + def visit_Getattr(self, node: nodes.Getattr, frame: Frame) -> None: + if self.environment.is_async: + self.write("(await auto_await(") + + self.write("environment.getattr(") + self.visit(node.node, frame) + self.write(f", {node.attr!r})") + + if self.environment.is_async: + self.write("))") + + @optimizeconst + def visit_Getitem(self, node: nodes.Getitem, frame: Frame) -> None: + # slices bypass the environment getitem method. + if isinstance(node.arg, nodes.Slice): + self.visit(node.node, frame) + self.write("[") + self.visit(node.arg, frame) + self.write("]") + else: + if self.environment.is_async: + self.write("(await auto_await(") + + self.write("environment.getitem(") + self.visit(node.node, frame) + self.write(", ") + self.visit(node.arg, frame) + self.write(")") + + if self.environment.is_async: + self.write("))") + + def visit_Slice(self, node: nodes.Slice, frame: Frame) -> None: + if node.start is not None: + self.visit(node.start, frame) + self.write(":") + if node.stop is not None: + self.visit(node.stop, frame) + if node.step is not None: + self.write(":") + self.visit(node.step, frame) + + @contextmanager + def _filter_test_common( + self, node: t.Union[nodes.Filter, nodes.Test], frame: Frame, is_filter: bool + ) -> t.Iterator[None]: + if self.environment.is_async: + self.write("(await auto_await(") + + if is_filter: + self.write(f"{self.filters[node.name]}(") + func = self.environment.filters.get(node.name) + else: + self.write(f"{self.tests[node.name]}(") + func = self.environment.tests.get(node.name) + + # When inside an If or CondExpr frame, allow the filter to be + # undefined at compile time and only raise an error if it's + # actually called at runtime. See pull_dependencies. + if func is None and not frame.soft_frame: + type_name = "filter" if is_filter else "test" + self.fail(f"No {type_name} named {node.name!r}.", node.lineno) + + pass_arg = { + _PassArg.context: "context", + _PassArg.eval_context: "context.eval_ctx", + _PassArg.environment: "environment", + }.get( + _PassArg.from_obj(func) # type: ignore + ) + + if pass_arg is not None: + self.write(f"{pass_arg}, ") + + # Back to the visitor function to handle visiting the target of + # the filter or test. + yield + + self.signature(node, frame) + self.write(")") + + if self.environment.is_async: + self.write("))") + + @optimizeconst + def visit_Filter(self, node: nodes.Filter, frame: Frame) -> None: + with self._filter_test_common(node, frame, True): + # if the filter node is None we are inside a filter block + # and want to write to the current buffer + if node.node is not None: + self.visit(node.node, frame) + elif frame.eval_ctx.volatile: + self.write( + f"(Markup(concat({frame.buffer}))" + f" if context.eval_ctx.autoescape else concat({frame.buffer}))" + ) + elif frame.eval_ctx.autoescape: + self.write(f"Markup(concat({frame.buffer}))") + else: + self.write(f"concat({frame.buffer})") + + @optimizeconst + def visit_Test(self, node: nodes.Test, frame: Frame) -> None: + with self._filter_test_common(node, frame, False): + self.visit(node.node, frame) + + @optimizeconst + def visit_CondExpr(self, node: nodes.CondExpr, frame: Frame) -> None: + frame = frame.soft() + + def write_expr2() -> None: + if node.expr2 is not None: + self.visit(node.expr2, frame) + return + + self.write( + f'cond_expr_undefined("the inline if-expression on' + f" {self.position(node)} evaluated to false and no else" + f' section was defined.")' + ) + + self.write("(") + self.visit(node.expr1, frame) + self.write(" if ") + self.visit(node.test, frame) + self.write(" else ") + write_expr2() + self.write(")") + + @optimizeconst + def visit_Call( + self, node: nodes.Call, frame: Frame, forward_caller: bool = False + ) -> None: + if self.environment.is_async: + self.write("(await auto_await(") + if self.environment.sandboxed: + self.write("environment.call(context, ") + else: + self.write("context.call(") + self.visit(node.node, frame) + extra_kwargs = {"caller": "caller"} if forward_caller else None + loop_kwargs = {"_loop_vars": "_loop_vars"} if frame.loop_frame else {} + block_kwargs = {"_block_vars": "_block_vars"} if frame.block_frame else {} + if extra_kwargs: + extra_kwargs.update(loop_kwargs, **block_kwargs) + elif loop_kwargs or block_kwargs: + extra_kwargs = dict(loop_kwargs, **block_kwargs) + self.signature(node, frame, extra_kwargs) + self.write(")") + if self.environment.is_async: + self.write("))") + + def visit_Keyword(self, node: nodes.Keyword, frame: Frame) -> None: + self.write(node.key + "=") + self.visit(node.value, frame) + + # -- Unused nodes for extensions + + def visit_MarkSafe(self, node: nodes.MarkSafe, frame: Frame) -> None: + self.write("Markup(") + self.visit(node.expr, frame) + self.write(")") + + def visit_MarkSafeIfAutoescape( + self, node: nodes.MarkSafeIfAutoescape, frame: Frame + ) -> None: + self.write("(Markup if context.eval_ctx.autoescape else identity)(") + self.visit(node.expr, frame) + self.write(")") + + def visit_EnvironmentAttribute( + self, node: nodes.EnvironmentAttribute, frame: Frame + ) -> None: + self.write("environment." + node.name) + + def visit_ExtensionAttribute( + self, node: nodes.ExtensionAttribute, frame: Frame + ) -> None: + self.write(f"environment.extensions[{node.identifier!r}].{node.name}") + + def visit_ImportedName(self, node: nodes.ImportedName, frame: Frame) -> None: + self.write(self.import_aliases[node.importname]) + + def visit_InternalName(self, node: nodes.InternalName, frame: Frame) -> None: + self.write(node.name) + + def visit_ContextReference( + self, node: nodes.ContextReference, frame: Frame + ) -> None: + self.write("context") + + def visit_DerivedContextReference( + self, node: nodes.DerivedContextReference, frame: Frame + ) -> None: + self.write(self.derive_context(frame)) + + def visit_Continue(self, node: nodes.Continue, frame: Frame) -> None: + self.writeline("continue", node) + + def visit_Break(self, node: nodes.Break, frame: Frame) -> None: + self.writeline("break", node) + + def visit_Scope(self, node: nodes.Scope, frame: Frame) -> None: + scope_frame = frame.inner() + scope_frame.symbols.analyze_node(node) + self.enter_frame(scope_frame) + self.blockvisit(node.body, scope_frame) + self.leave_frame(scope_frame) + + def visit_OverlayScope(self, node: nodes.OverlayScope, frame: Frame) -> None: + ctx = self.temporary_identifier() + self.writeline(f"{ctx} = {self.derive_context(frame)}") + self.writeline(f"{ctx}.vars = ") + self.visit(node.context, frame) + self.push_context_reference(ctx) + + scope_frame = frame.inner(isolated=True) + scope_frame.symbols.analyze_node(node) + self.enter_frame(scope_frame) + self.blockvisit(node.body, scope_frame) + self.leave_frame(scope_frame) + self.pop_context_reference() + + def visit_EvalContextModifier( + self, node: nodes.EvalContextModifier, frame: Frame + ) -> None: + for keyword in node.options: + self.writeline(f"context.eval_ctx.{keyword.key} = ") + self.visit(keyword.value, frame) + try: + val = keyword.value.as_const(frame.eval_ctx) + except nodes.Impossible: + frame.eval_ctx.volatile = True + else: + setattr(frame.eval_ctx, keyword.key, val) + + def visit_ScopedEvalContextModifier( + self, node: nodes.ScopedEvalContextModifier, frame: Frame + ) -> None: + old_ctx_name = self.temporary_identifier() + saved_ctx = frame.eval_ctx.save() + self.writeline(f"{old_ctx_name} = context.eval_ctx.save()") + self.visit_EvalContextModifier(node, frame) + for child in node.body: + self.visit(child, frame) + frame.eval_ctx.revert(saved_ctx) + self.writeline(f"context.eval_ctx.revert({old_ctx_name})") diff --git a/venv/lib/python3.12/site-packages/jinja2/constants.py b/venv/lib/python3.12/site-packages/jinja2/constants.py new file mode 100644 index 00000000..41a1c23b --- /dev/null +++ b/venv/lib/python3.12/site-packages/jinja2/constants.py @@ -0,0 +1,20 @@ +#: list of lorem ipsum words used by the lipsum() helper function +LOREM_IPSUM_WORDS = """\ +a ac accumsan ad adipiscing aenean aliquam aliquet amet ante aptent arcu at +auctor augue bibendum blandit class commodo condimentum congue consectetuer +consequat conubia convallis cras cubilia cum curabitur curae cursus dapibus +diam dictum dictumst dignissim dis dolor donec dui duis egestas eget eleifend +elementum elit enim erat eros est et etiam eu euismod facilisi facilisis fames +faucibus felis fermentum feugiat fringilla fusce gravida habitant habitasse hac +hendrerit hymenaeos iaculis id imperdiet in inceptos integer interdum ipsum +justo lacinia lacus laoreet lectus leo libero ligula litora lobortis lorem +luctus maecenas magna magnis malesuada massa mattis mauris metus mi molestie +mollis montes morbi mus nam nascetur natoque nec neque netus nibh nisi nisl non +nonummy nostra nulla nullam nunc odio orci ornare parturient pede pellentesque +penatibus per pharetra phasellus placerat platea porta porttitor posuere +potenti praesent pretium primis proin pulvinar purus quam quis quisque rhoncus +ridiculus risus rutrum sagittis sapien scelerisque sed sem semper senectus sit +sociis sociosqu sodales sollicitudin suscipit suspendisse taciti tellus tempor +tempus tincidunt torquent tortor tristique turpis ullamcorper ultrices +ultricies urna ut varius vehicula vel velit venenatis vestibulum vitae vivamus +viverra volutpat vulputate""" diff --git a/venv/lib/python3.12/site-packages/jinja2/debug.py b/venv/lib/python3.12/site-packages/jinja2/debug.py new file mode 100644 index 00000000..eeeeee78 --- /dev/null +++ b/venv/lib/python3.12/site-packages/jinja2/debug.py @@ -0,0 +1,191 @@ +import sys +import typing as t +from types import CodeType +from types import TracebackType + +from .exceptions import TemplateSyntaxError +from .utils import internal_code +from .utils import missing + +if t.TYPE_CHECKING: + from .runtime import Context + + +def rewrite_traceback_stack(source: t.Optional[str] = None) -> BaseException: + """Rewrite the current exception to replace any tracebacks from + within compiled template code with tracebacks that look like they + came from the template source. + + This must be called within an ``except`` block. + + :param source: For ``TemplateSyntaxError``, the original source if + known. + :return: The original exception with the rewritten traceback. + """ + _, exc_value, tb = sys.exc_info() + exc_value = t.cast(BaseException, exc_value) + tb = t.cast(TracebackType, tb) + + if isinstance(exc_value, TemplateSyntaxError) and not exc_value.translated: + exc_value.translated = True + exc_value.source = source + # Remove the old traceback, otherwise the frames from the + # compiler still show up. + exc_value.with_traceback(None) + # Outside of runtime, so the frame isn't executing template + # code, but it still needs to point at the template. + tb = fake_traceback( + exc_value, None, exc_value.filename or "", exc_value.lineno + ) + else: + # Skip the frame for the render function. + tb = tb.tb_next + + stack = [] + + # Build the stack of traceback object, replacing any in template + # code with the source file and line information. + while tb is not None: + # Skip frames decorated with @internalcode. These are internal + # calls that aren't useful in template debugging output. + if tb.tb_frame.f_code in internal_code: + tb = tb.tb_next + continue + + template = tb.tb_frame.f_globals.get("__jinja_template__") + + if template is not None: + lineno = template.get_corresponding_lineno(tb.tb_lineno) + fake_tb = fake_traceback(exc_value, tb, template.filename, lineno) + stack.append(fake_tb) + else: + stack.append(tb) + + tb = tb.tb_next + + tb_next = None + + # Assign tb_next in reverse to avoid circular references. + for tb in reversed(stack): + tb.tb_next = tb_next + tb_next = tb + + return exc_value.with_traceback(tb_next) + + +def fake_traceback( # type: ignore + exc_value: BaseException, tb: t.Optional[TracebackType], filename: str, lineno: int +) -> TracebackType: + """Produce a new traceback object that looks like it came from the + template source instead of the compiled code. The filename, line + number, and location name will point to the template, and the local + variables will be the current template context. + + :param exc_value: The original exception to be re-raised to create + the new traceback. + :param tb: The original traceback to get the local variables and + code info from. + :param filename: The template filename. + :param lineno: The line number in the template source. + """ + if tb is not None: + # Replace the real locals with the context that would be + # available at that point in the template. + locals = get_template_locals(tb.tb_frame.f_locals) + locals.pop("__jinja_exception__", None) + else: + locals = {} + + globals = { + "__name__": filename, + "__file__": filename, + "__jinja_exception__": exc_value, + } + # Raise an exception at the correct line number. + code: CodeType = compile( + "\n" * (lineno - 1) + "raise __jinja_exception__", filename, "exec" + ) + + # Build a new code object that points to the template file and + # replaces the location with a block name. + location = "template" + + if tb is not None: + function = tb.tb_frame.f_code.co_name + + if function == "root": + location = "top-level template code" + elif function.startswith("block_"): + location = f"block {function[6:]!r}" + + if sys.version_info >= (3, 8): + code = code.replace(co_name=location) + else: + code = CodeType( + code.co_argcount, + code.co_kwonlyargcount, + code.co_nlocals, + code.co_stacksize, + code.co_flags, + code.co_code, + code.co_consts, + code.co_names, + code.co_varnames, + code.co_filename, + location, + code.co_firstlineno, + code.co_lnotab, + code.co_freevars, + code.co_cellvars, + ) + + # Execute the new code, which is guaranteed to raise, and return + # the new traceback without this frame. + try: + exec(code, globals, locals) + except BaseException: + return sys.exc_info()[2].tb_next # type: ignore + + +def get_template_locals(real_locals: t.Mapping[str, t.Any]) -> t.Dict[str, t.Any]: + """Based on the runtime locals, get the context that would be + available at that point in the template. + """ + # Start with the current template context. + ctx: t.Optional[Context] = real_locals.get("context") + + if ctx is not None: + data: t.Dict[str, t.Any] = ctx.get_all().copy() + else: + data = {} + + # Might be in a derived context that only sets local variables + # rather than pushing a context. Local variables follow the scheme + # l_depth_name. Find the highest-depth local that has a value for + # each name. + local_overrides: t.Dict[str, t.Tuple[int, t.Any]] = {} + + for name, value in real_locals.items(): + if not name.startswith("l_") or value is missing: + # Not a template variable, or no longer relevant. + continue + + try: + _, depth_str, name = name.split("_", 2) + depth = int(depth_str) + except ValueError: + continue + + cur_depth = local_overrides.get(name, (-1,))[0] + + if cur_depth < depth: + local_overrides[name] = (depth, value) + + # Modify the context with any derived context. + for name, (_, value) in local_overrides.items(): + if value is missing: + data.pop(name, None) + else: + data[name] = value + + return data diff --git a/venv/lib/python3.12/site-packages/jinja2/defaults.py b/venv/lib/python3.12/site-packages/jinja2/defaults.py new file mode 100644 index 00000000..638cad3d --- /dev/null +++ b/venv/lib/python3.12/site-packages/jinja2/defaults.py @@ -0,0 +1,48 @@ +import typing as t + +from .filters import FILTERS as DEFAULT_FILTERS # noqa: F401 +from .tests import TESTS as DEFAULT_TESTS # noqa: F401 +from .utils import Cycler +from .utils import generate_lorem_ipsum +from .utils import Joiner +from .utils import Namespace + +if t.TYPE_CHECKING: + import typing_extensions as te + +# defaults for the parser / lexer +BLOCK_START_STRING = "{%" +BLOCK_END_STRING = "%}" +VARIABLE_START_STRING = "{{" +VARIABLE_END_STRING = "}}" +COMMENT_START_STRING = "{#" +COMMENT_END_STRING = "#}" +LINE_STATEMENT_PREFIX: t.Optional[str] = None +LINE_COMMENT_PREFIX: t.Optional[str] = None +TRIM_BLOCKS = False +LSTRIP_BLOCKS = False +NEWLINE_SEQUENCE: "te.Literal['\\n', '\\r\\n', '\\r']" = "\n" +KEEP_TRAILING_NEWLINE = False + +# default filters, tests and namespace + +DEFAULT_NAMESPACE = { + "range": range, + "dict": dict, + "lipsum": generate_lorem_ipsum, + "cycler": Cycler, + "joiner": Joiner, + "namespace": Namespace, +} + +# default policies +DEFAULT_POLICIES: t.Dict[str, t.Any] = { + "compiler.ascii_str": True, + "urlize.rel": "noopener", + "urlize.target": None, + "urlize.extra_schemes": None, + "truncate.leeway": 5, + "json.dumps_function": None, + "json.dumps_kwargs": {"sort_keys": True}, + "ext.i18n.trimmed": False, +} diff --git a/venv/lib/python3.12/site-packages/jinja2/environment.py b/venv/lib/python3.12/site-packages/jinja2/environment.py new file mode 100644 index 00000000..0fc6e5be --- /dev/null +++ b/venv/lib/python3.12/site-packages/jinja2/environment.py @@ -0,0 +1,1672 @@ +"""Classes for managing templates and their runtime and compile time +options. +""" + +import os +import typing +import typing as t +import weakref +from collections import ChainMap +from functools import lru_cache +from functools import partial +from functools import reduce +from types import CodeType + +from markupsafe import Markup + +from . import nodes +from .compiler import CodeGenerator +from .compiler import generate +from .defaults import BLOCK_END_STRING +from .defaults import BLOCK_START_STRING +from .defaults import COMMENT_END_STRING +from .defaults import COMMENT_START_STRING +from .defaults import DEFAULT_FILTERS # type: ignore[attr-defined] +from .defaults import DEFAULT_NAMESPACE +from .defaults import DEFAULT_POLICIES +from .defaults import DEFAULT_TESTS # type: ignore[attr-defined] +from .defaults import KEEP_TRAILING_NEWLINE +from .defaults import LINE_COMMENT_PREFIX +from .defaults import LINE_STATEMENT_PREFIX +from .defaults import LSTRIP_BLOCKS +from .defaults import NEWLINE_SEQUENCE +from .defaults import TRIM_BLOCKS +from .defaults import VARIABLE_END_STRING +from .defaults import VARIABLE_START_STRING +from .exceptions import TemplateNotFound +from .exceptions import TemplateRuntimeError +from .exceptions import TemplatesNotFound +from .exceptions import TemplateSyntaxError +from .exceptions import UndefinedError +from .lexer import get_lexer +from .lexer import Lexer +from .lexer import TokenStream +from .nodes import EvalContext +from .parser import Parser +from .runtime import Context +from .runtime import new_context +from .runtime import Undefined +from .utils import _PassArg +from .utils import concat +from .utils import consume +from .utils import import_string +from .utils import internalcode +from .utils import LRUCache +from .utils import missing + +if t.TYPE_CHECKING: + import typing_extensions as te + + from .bccache import BytecodeCache + from .ext import Extension + from .loaders import BaseLoader + +_env_bound = t.TypeVar("_env_bound", bound="Environment") + + +# for direct template usage we have up to ten living environments +@lru_cache(maxsize=10) +def get_spontaneous_environment(cls: t.Type[_env_bound], *args: t.Any) -> _env_bound: + """Return a new spontaneous environment. A spontaneous environment + is used for templates created directly rather than through an + existing environment. + + :param cls: Environment class to create. + :param args: Positional arguments passed to environment. + """ + env = cls(*args) + env.shared = True + return env + + +def create_cache( + size: int, +) -> t.Optional[t.MutableMapping[t.Tuple["weakref.ref[t.Any]", str], "Template"]]: + """Return the cache class for the given size.""" + if size == 0: + return None + + if size < 0: + return {} + + return LRUCache(size) # type: ignore + + +def copy_cache( + cache: t.Optional[t.MutableMapping[t.Any, t.Any]], +) -> t.Optional[t.MutableMapping[t.Tuple["weakref.ref[t.Any]", str], "Template"]]: + """Create an empty copy of the given cache.""" + if cache is None: + return None + + if type(cache) is dict: # noqa E721 + return {} + + return LRUCache(cache.capacity) # type: ignore + + +def load_extensions( + environment: "Environment", + extensions: t.Sequence[t.Union[str, t.Type["Extension"]]], +) -> t.Dict[str, "Extension"]: + """Load the extensions from the list and bind it to the environment. + Returns a dict of instantiated extensions. + """ + result = {} + + for extension in extensions: + if isinstance(extension, str): + extension = t.cast(t.Type["Extension"], import_string(extension)) + + result[extension.identifier] = extension(environment) + + return result + + +def _environment_config_check(environment: _env_bound) -> _env_bound: + """Perform a sanity check on the environment.""" + assert issubclass( + environment.undefined, Undefined + ), "'undefined' must be a subclass of 'jinja2.Undefined'." + assert ( + environment.block_start_string + != environment.variable_start_string + != environment.comment_start_string + ), "block, variable and comment start strings must be different." + assert environment.newline_sequence in { + "\r", + "\r\n", + "\n", + }, "'newline_sequence' must be one of '\\n', '\\r\\n', or '\\r'." + return environment + + +class Environment: + r"""The core component of Jinja is the `Environment`. It contains + important shared variables like configuration, filters, tests, + globals and others. Instances of this class may be modified if + they are not shared and if no template was loaded so far. + Modifications on environments after the first template was loaded + will lead to surprising effects and undefined behavior. + + Here are the possible initialization parameters: + + `block_start_string` + The string marking the beginning of a block. Defaults to ``'{%'``. + + `block_end_string` + The string marking the end of a block. Defaults to ``'%}'``. + + `variable_start_string` + The string marking the beginning of a print statement. + Defaults to ``'{{'``. + + `variable_end_string` + The string marking the end of a print statement. Defaults to + ``'}}'``. + + `comment_start_string` + The string marking the beginning of a comment. Defaults to ``'{#'``. + + `comment_end_string` + The string marking the end of a comment. Defaults to ``'#}'``. + + `line_statement_prefix` + If given and a string, this will be used as prefix for line based + statements. See also :ref:`line-statements`. + + `line_comment_prefix` + If given and a string, this will be used as prefix for line based + comments. See also :ref:`line-statements`. + + .. versionadded:: 2.2 + + `trim_blocks` + If this is set to ``True`` the first newline after a block is + removed (block, not variable tag!). Defaults to `False`. + + `lstrip_blocks` + If this is set to ``True`` leading spaces and tabs are stripped + from the start of a line to a block. Defaults to `False`. + + `newline_sequence` + The sequence that starts a newline. Must be one of ``'\r'``, + ``'\n'`` or ``'\r\n'``. The default is ``'\n'`` which is a + useful default for Linux and OS X systems as well as web + applications. + + `keep_trailing_newline` + Preserve the trailing newline when rendering templates. + The default is ``False``, which causes a single newline, + if present, to be stripped from the end of the template. + + .. versionadded:: 2.7 + + `extensions` + List of Jinja extensions to use. This can either be import paths + as strings or extension classes. For more information have a + look at :ref:`the extensions documentation `. + + `optimized` + should the optimizer be enabled? Default is ``True``. + + `undefined` + :class:`Undefined` or a subclass of it that is used to represent + undefined values in the template. + + `finalize` + A callable that can be used to process the result of a variable + expression before it is output. For example one can convert + ``None`` implicitly into an empty string here. + + `autoescape` + If set to ``True`` the XML/HTML autoescaping feature is enabled by + default. For more details about autoescaping see + :class:`~markupsafe.Markup`. As of Jinja 2.4 this can also + be a callable that is passed the template name and has to + return ``True`` or ``False`` depending on autoescape should be + enabled by default. + + .. versionchanged:: 2.4 + `autoescape` can now be a function + + `loader` + The template loader for this environment. + + `cache_size` + The size of the cache. Per default this is ``400`` which means + that if more than 400 templates are loaded the loader will clean + out the least recently used template. If the cache size is set to + ``0`` templates are recompiled all the time, if the cache size is + ``-1`` the cache will not be cleaned. + + .. versionchanged:: 2.8 + The cache size was increased to 400 from a low 50. + + `auto_reload` + Some loaders load templates from locations where the template + sources may change (ie: file system or database). If + ``auto_reload`` is set to ``True`` (default) every time a template is + requested the loader checks if the source changed and if yes, it + will reload the template. For higher performance it's possible to + disable that. + + `bytecode_cache` + If set to a bytecode cache object, this object will provide a + cache for the internal Jinja bytecode so that templates don't + have to be parsed if they were not changed. + + See :ref:`bytecode-cache` for more information. + + `enable_async` + If set to true this enables async template execution which + allows using async functions and generators. + """ + + #: if this environment is sandboxed. Modifying this variable won't make + #: the environment sandboxed though. For a real sandboxed environment + #: have a look at jinja2.sandbox. This flag alone controls the code + #: generation by the compiler. + sandboxed = False + + #: True if the environment is just an overlay + overlayed = False + + #: the environment this environment is linked to if it is an overlay + linked_to: t.Optional["Environment"] = None + + #: shared environments have this set to `True`. A shared environment + #: must not be modified + shared = False + + #: the class that is used for code generation. See + #: :class:`~jinja2.compiler.CodeGenerator` for more information. + code_generator_class: t.Type["CodeGenerator"] = CodeGenerator + + concat = "".join + + #: the context class that is used for templates. See + #: :class:`~jinja2.runtime.Context` for more information. + context_class: t.Type[Context] = Context + + template_class: t.Type["Template"] + + def __init__( + self, + block_start_string: str = BLOCK_START_STRING, + block_end_string: str = BLOCK_END_STRING, + variable_start_string: str = VARIABLE_START_STRING, + variable_end_string: str = VARIABLE_END_STRING, + comment_start_string: str = COMMENT_START_STRING, + comment_end_string: str = COMMENT_END_STRING, + line_statement_prefix: t.Optional[str] = LINE_STATEMENT_PREFIX, + line_comment_prefix: t.Optional[str] = LINE_COMMENT_PREFIX, + trim_blocks: bool = TRIM_BLOCKS, + lstrip_blocks: bool = LSTRIP_BLOCKS, + newline_sequence: "te.Literal['\\n', '\\r\\n', '\\r']" = NEWLINE_SEQUENCE, + keep_trailing_newline: bool = KEEP_TRAILING_NEWLINE, + extensions: t.Sequence[t.Union[str, t.Type["Extension"]]] = (), + optimized: bool = True, + undefined: t.Type[Undefined] = Undefined, + finalize: t.Optional[t.Callable[..., t.Any]] = None, + autoescape: t.Union[bool, t.Callable[[t.Optional[str]], bool]] = False, + loader: t.Optional["BaseLoader"] = None, + cache_size: int = 400, + auto_reload: bool = True, + bytecode_cache: t.Optional["BytecodeCache"] = None, + enable_async: bool = False, + ): + # !!Important notice!! + # The constructor accepts quite a few arguments that should be + # passed by keyword rather than position. However it's important to + # not change the order of arguments because it's used at least + # internally in those cases: + # - spontaneous environments (i18n extension and Template) + # - unittests + # If parameter changes are required only add parameters at the end + # and don't change the arguments (or the defaults!) of the arguments + # existing already. + + # lexer / parser information + self.block_start_string = block_start_string + self.block_end_string = block_end_string + self.variable_start_string = variable_start_string + self.variable_end_string = variable_end_string + self.comment_start_string = comment_start_string + self.comment_end_string = comment_end_string + self.line_statement_prefix = line_statement_prefix + self.line_comment_prefix = line_comment_prefix + self.trim_blocks = trim_blocks + self.lstrip_blocks = lstrip_blocks + self.newline_sequence = newline_sequence + self.keep_trailing_newline = keep_trailing_newline + + # runtime information + self.undefined: t.Type[Undefined] = undefined + self.optimized = optimized + self.finalize = finalize + self.autoescape = autoescape + + # defaults + self.filters = DEFAULT_FILTERS.copy() + self.tests = DEFAULT_TESTS.copy() + self.globals = DEFAULT_NAMESPACE.copy() + + # set the loader provided + self.loader = loader + self.cache = create_cache(cache_size) + self.bytecode_cache = bytecode_cache + self.auto_reload = auto_reload + + # configurable policies + self.policies = DEFAULT_POLICIES.copy() + + # load extensions + self.extensions = load_extensions(self, extensions) + + self.is_async = enable_async + _environment_config_check(self) + + def add_extension(self, extension: t.Union[str, t.Type["Extension"]]) -> None: + """Adds an extension after the environment was created. + + .. versionadded:: 2.5 + """ + self.extensions.update(load_extensions(self, [extension])) + + def extend(self, **attributes: t.Any) -> None: + """Add the items to the instance of the environment if they do not exist + yet. This is used by :ref:`extensions ` to register + callbacks and configuration values without breaking inheritance. + """ + for key, value in attributes.items(): + if not hasattr(self, key): + setattr(self, key, value) + + def overlay( + self, + block_start_string: str = missing, + block_end_string: str = missing, + variable_start_string: str = missing, + variable_end_string: str = missing, + comment_start_string: str = missing, + comment_end_string: str = missing, + line_statement_prefix: t.Optional[str] = missing, + line_comment_prefix: t.Optional[str] = missing, + trim_blocks: bool = missing, + lstrip_blocks: bool = missing, + newline_sequence: "te.Literal['\\n', '\\r\\n', '\\r']" = missing, + keep_trailing_newline: bool = missing, + extensions: t.Sequence[t.Union[str, t.Type["Extension"]]] = missing, + optimized: bool = missing, + undefined: t.Type[Undefined] = missing, + finalize: t.Optional[t.Callable[..., t.Any]] = missing, + autoescape: t.Union[bool, t.Callable[[t.Optional[str]], bool]] = missing, + loader: t.Optional["BaseLoader"] = missing, + cache_size: int = missing, + auto_reload: bool = missing, + bytecode_cache: t.Optional["BytecodeCache"] = missing, + enable_async: bool = missing, + ) -> "te.Self": + """Create a new overlay environment that shares all the data with the + current environment except for cache and the overridden attributes. + Extensions cannot be removed for an overlayed environment. An overlayed + environment automatically gets all the extensions of the environment it + is linked to plus optional extra extensions. + + Creating overlays should happen after the initial environment was set + up completely. Not all attributes are truly linked, some are just + copied over so modifications on the original environment may not shine + through. + + .. versionchanged:: 3.1.5 + ``enable_async`` is applied correctly. + + .. versionchanged:: 3.1.2 + Added the ``newline_sequence``, ``keep_trailing_newline``, + and ``enable_async`` parameters to match ``__init__``. + """ + args = dict(locals()) + del args["self"], args["cache_size"], args["extensions"], args["enable_async"] + + rv = object.__new__(self.__class__) + rv.__dict__.update(self.__dict__) + rv.overlayed = True + rv.linked_to = self + + for key, value in args.items(): + if value is not missing: + setattr(rv, key, value) + + if cache_size is not missing: + rv.cache = create_cache(cache_size) + else: + rv.cache = copy_cache(self.cache) + + rv.extensions = {} + for key, value in self.extensions.items(): + rv.extensions[key] = value.bind(rv) + if extensions is not missing: + rv.extensions.update(load_extensions(rv, extensions)) + + if enable_async is not missing: + rv.is_async = enable_async + + return _environment_config_check(rv) + + @property + def lexer(self) -> Lexer: + """The lexer for this environment.""" + return get_lexer(self) + + def iter_extensions(self) -> t.Iterator["Extension"]: + """Iterates over the extensions by priority.""" + return iter(sorted(self.extensions.values(), key=lambda x: x.priority)) + + def getitem( + self, obj: t.Any, argument: t.Union[str, t.Any] + ) -> t.Union[t.Any, Undefined]: + """Get an item or attribute of an object but prefer the item.""" + try: + return obj[argument] + except (AttributeError, TypeError, LookupError): + if isinstance(argument, str): + try: + attr = str(argument) + except Exception: + pass + else: + try: + return getattr(obj, attr) + except AttributeError: + pass + return self.undefined(obj=obj, name=argument) + + def getattr(self, obj: t.Any, attribute: str) -> t.Any: + """Get an item or attribute of an object but prefer the attribute. + Unlike :meth:`getitem` the attribute *must* be a string. + """ + try: + return getattr(obj, attribute) + except AttributeError: + pass + try: + return obj[attribute] + except (TypeError, LookupError, AttributeError): + return self.undefined(obj=obj, name=attribute) + + def _filter_test_common( + self, + name: t.Union[str, Undefined], + value: t.Any, + args: t.Optional[t.Sequence[t.Any]], + kwargs: t.Optional[t.Mapping[str, t.Any]], + context: t.Optional[Context], + eval_ctx: t.Optional[EvalContext], + is_filter: bool, + ) -> t.Any: + if is_filter: + env_map = self.filters + type_name = "filter" + else: + env_map = self.tests + type_name = "test" + + func = env_map.get(name) # type: ignore + + if func is None: + msg = f"No {type_name} named {name!r}." + + if isinstance(name, Undefined): + try: + name._fail_with_undefined_error() + except Exception as e: + msg = f"{msg} ({e}; did you forget to quote the callable name?)" + + raise TemplateRuntimeError(msg) + + args = [value, *(args if args is not None else ())] + kwargs = kwargs if kwargs is not None else {} + pass_arg = _PassArg.from_obj(func) + + if pass_arg is _PassArg.context: + if context is None: + raise TemplateRuntimeError( + f"Attempted to invoke a context {type_name} without context." + ) + + args.insert(0, context) + elif pass_arg is _PassArg.eval_context: + if eval_ctx is None: + if context is not None: + eval_ctx = context.eval_ctx + else: + eval_ctx = EvalContext(self) + + args.insert(0, eval_ctx) + elif pass_arg is _PassArg.environment: + args.insert(0, self) + + return func(*args, **kwargs) + + def call_filter( + self, + name: str, + value: t.Any, + args: t.Optional[t.Sequence[t.Any]] = None, + kwargs: t.Optional[t.Mapping[str, t.Any]] = None, + context: t.Optional[Context] = None, + eval_ctx: t.Optional[EvalContext] = None, + ) -> t.Any: + """Invoke a filter on a value the same way the compiler does. + + This might return a coroutine if the filter is running from an + environment in async mode and the filter supports async + execution. It's your responsibility to await this if needed. + + .. versionadded:: 2.7 + """ + return self._filter_test_common( + name, value, args, kwargs, context, eval_ctx, True + ) + + def call_test( + self, + name: str, + value: t.Any, + args: t.Optional[t.Sequence[t.Any]] = None, + kwargs: t.Optional[t.Mapping[str, t.Any]] = None, + context: t.Optional[Context] = None, + eval_ctx: t.Optional[EvalContext] = None, + ) -> t.Any: + """Invoke a test on a value the same way the compiler does. + + This might return a coroutine if the test is running from an + environment in async mode and the test supports async execution. + It's your responsibility to await this if needed. + + .. versionchanged:: 3.0 + Tests support ``@pass_context``, etc. decorators. Added + the ``context`` and ``eval_ctx`` parameters. + + .. versionadded:: 2.7 + """ + return self._filter_test_common( + name, value, args, kwargs, context, eval_ctx, False + ) + + @internalcode + def parse( + self, + source: str, + name: t.Optional[str] = None, + filename: t.Optional[str] = None, + ) -> nodes.Template: + """Parse the sourcecode and return the abstract syntax tree. This + tree of nodes is used by the compiler to convert the template into + executable source- or bytecode. This is useful for debugging or to + extract information from templates. + + If you are :ref:`developing Jinja extensions ` + this gives you a good overview of the node tree generated. + """ + try: + return self._parse(source, name, filename) + except TemplateSyntaxError: + self.handle_exception(source=source) + + def _parse( + self, source: str, name: t.Optional[str], filename: t.Optional[str] + ) -> nodes.Template: + """Internal parsing function used by `parse` and `compile`.""" + return Parser(self, source, name, filename).parse() + + def lex( + self, + source: str, + name: t.Optional[str] = None, + filename: t.Optional[str] = None, + ) -> t.Iterator[t.Tuple[int, str, str]]: + """Lex the given sourcecode and return a generator that yields + tokens as tuples in the form ``(lineno, token_type, value)``. + This can be useful for :ref:`extension development ` + and debugging templates. + + This does not perform preprocessing. If you want the preprocessing + of the extensions to be applied you have to filter source through + the :meth:`preprocess` method. + """ + source = str(source) + try: + return self.lexer.tokeniter(source, name, filename) + except TemplateSyntaxError: + self.handle_exception(source=source) + + def preprocess( + self, + source: str, + name: t.Optional[str] = None, + filename: t.Optional[str] = None, + ) -> str: + """Preprocesses the source with all extensions. This is automatically + called for all parsing and compiling methods but *not* for :meth:`lex` + because there you usually only want the actual source tokenized. + """ + return reduce( + lambda s, e: e.preprocess(s, name, filename), + self.iter_extensions(), + str(source), + ) + + def _tokenize( + self, + source: str, + name: t.Optional[str], + filename: t.Optional[str] = None, + state: t.Optional[str] = None, + ) -> TokenStream: + """Called by the parser to do the preprocessing and filtering + for all the extensions. Returns a :class:`~jinja2.lexer.TokenStream`. + """ + source = self.preprocess(source, name, filename) + stream = self.lexer.tokenize(source, name, filename, state) + + for ext in self.iter_extensions(): + stream = ext.filter_stream(stream) # type: ignore + + if not isinstance(stream, TokenStream): + stream = TokenStream(stream, name, filename) + + return stream + + def _generate( + self, + source: nodes.Template, + name: t.Optional[str], + filename: t.Optional[str], + defer_init: bool = False, + ) -> str: + """Internal hook that can be overridden to hook a different generate + method in. + + .. versionadded:: 2.5 + """ + return generate( # type: ignore + source, + self, + name, + filename, + defer_init=defer_init, + optimized=self.optimized, + ) + + def _compile(self, source: str, filename: str) -> CodeType: + """Internal hook that can be overridden to hook a different compile + method in. + + .. versionadded:: 2.5 + """ + return compile(source, filename, "exec") + + @typing.overload + def compile( + self, + source: t.Union[str, nodes.Template], + name: t.Optional[str] = None, + filename: t.Optional[str] = None, + raw: "te.Literal[False]" = False, + defer_init: bool = False, + ) -> CodeType: ... + + @typing.overload + def compile( + self, + source: t.Union[str, nodes.Template], + name: t.Optional[str] = None, + filename: t.Optional[str] = None, + raw: "te.Literal[True]" = ..., + defer_init: bool = False, + ) -> str: ... + + @internalcode + def compile( + self, + source: t.Union[str, nodes.Template], + name: t.Optional[str] = None, + filename: t.Optional[str] = None, + raw: bool = False, + defer_init: bool = False, + ) -> t.Union[str, CodeType]: + """Compile a node or template source code. The `name` parameter is + the load name of the template after it was joined using + :meth:`join_path` if necessary, not the filename on the file system. + the `filename` parameter is the estimated filename of the template on + the file system. If the template came from a database or memory this + can be omitted. + + The return value of this method is a python code object. If the `raw` + parameter is `True` the return value will be a string with python + code equivalent to the bytecode returned otherwise. This method is + mainly used internally. + + `defer_init` is use internally to aid the module code generator. This + causes the generated code to be able to import without the global + environment variable to be set. + + .. versionadded:: 2.4 + `defer_init` parameter added. + """ + source_hint = None + try: + if isinstance(source, str): + source_hint = source + source = self._parse(source, name, filename) + source = self._generate(source, name, filename, defer_init=defer_init) + if raw: + return source + if filename is None: + filename = "