diff --git a/venv/bin/Activate.ps1 b/venv/bin/Activate.ps1 new file mode 100644 index 000000000..b49d77ba4 --- /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 000000000..3ff9d04a7 --- /dev/null +++ b/venv/bin/activate @@ -0,0 +1,69 @@ +# 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 + + # This should detect bash and zsh, which have a hash command that must + # be called to get it to forget past commands. Without forgetting + # past commands the $PATH changes we made may not be respected + if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then + hash -r 2> /dev/null + fi + + 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 + +VIRTUAL_ENV="/Users/u6871990/Downloads/ACCESS-NRI/access-hive-dev/access-hive.github.io/venv" +export VIRTUAL_ENV + +_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 + +# This should detect bash and zsh, which have a hash command that must +# be called to get it to forget past commands. Without forgetting +# past commands the $PATH changes we made may not be respected +if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then + hash -r 2> /dev/null +fi diff --git a/venv/bin/activate.csh b/venv/bin/activate.csh new file mode 100644 index 000000000..aa57720f7 --- /dev/null +++ b/venv/bin/activate.csh @@ -0,0 +1,26 @@ +# 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/u6871990/Downloads/ACCESS-NRI/access-hive-dev/access-hive.github.io/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 000000000..bc27f7b28 --- /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/u6871990/Downloads/ACCESS-NRI/access-hive-dev/access-hive.github.io/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 000000000..9343fdd26 --- /dev/null +++ b/venv/bin/ghp-import @@ -0,0 +1,8 @@ +#!/Users/u6871990/Downloads/ACCESS-NRI/access-hive-dev/access-hive.github.io/venv/bin/python3 +# -*- 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 000000000..3931aa592 --- /dev/null +++ b/venv/bin/markdown_py @@ -0,0 +1,8 @@ +#!/Users/u6871990/Downloads/ACCESS-NRI/access-hive-dev/access-hive.github.io/venv/bin/python3 +# -*- 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 000000000..c659eca6f --- /dev/null +++ b/venv/bin/mkdocs @@ -0,0 +1,8 @@ +#!/Users/u6871990/Downloads/ACCESS-NRI/access-hive-dev/access-hive.github.io/venv/bin/python3 +# -*- 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/normalizer b/venv/bin/normalizer new file mode 100755 index 000000000..4f703ee7e --- /dev/null +++ b/venv/bin/normalizer @@ -0,0 +1,8 @@ +#!/Users/u6871990/Downloads/ACCESS-NRI/access-hive-dev/access-hive.github.io/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from charset_normalizer.cli.normalizer import cli_detect +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(cli_detect()) diff --git a/venv/bin/pip b/venv/bin/pip new file mode 100755 index 000000000..59855426a --- /dev/null +++ b/venv/bin/pip @@ -0,0 +1,8 @@ +#!/Users/u6871990/Downloads/ACCESS-NRI/access-hive-dev/access-hive.github.io/venv/bin/python3 +# -*- 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 000000000..59855426a --- /dev/null +++ b/venv/bin/pip3 @@ -0,0 +1,8 @@ +#!/Users/u6871990/Downloads/ACCESS-NRI/access-hive-dev/access-hive.github.io/venv/bin/python3 +# -*- 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.10 b/venv/bin/pip3.10 new file mode 100755 index 000000000..59855426a --- /dev/null +++ b/venv/bin/pip3.10 @@ -0,0 +1,8 @@ +#!/Users/u6871990/Downloads/ACCESS-NRI/access-hive-dev/access-hive.github.io/venv/bin/python3 +# -*- 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 000000000..0fb607fb9 --- /dev/null +++ b/venv/bin/pybabel @@ -0,0 +1,8 @@ +#!/Users/u6871990/Downloads/ACCESS-NRI/access-hive-dev/access-hive.github.io/venv/bin/python3 +# -*- 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/pybtex b/venv/bin/pybtex new file mode 100755 index 000000000..418ac84ae --- /dev/null +++ b/venv/bin/pybtex @@ -0,0 +1,8 @@ +#!/Users/u6871990/Downloads/ACCESS-NRI/access-hive-dev/access-hive.github.io/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pybtex.__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/pybtex-convert b/venv/bin/pybtex-convert new file mode 100755 index 000000000..8ef467ddc --- /dev/null +++ b/venv/bin/pybtex-convert @@ -0,0 +1,8 @@ +#!/Users/u6871990/Downloads/ACCESS-NRI/access-hive-dev/access-hive.github.io/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pybtex.database.convert.__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/pybtex-format b/venv/bin/pybtex-format new file mode 100755 index 000000000..2aa00e042 --- /dev/null +++ b/venv/bin/pybtex-format @@ -0,0 +1,8 @@ +#!/Users/u6871990/Downloads/ACCESS-NRI/access-hive-dev/access-hive.github.io/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pybtex.database.format.__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/pygmentize b/venv/bin/pygmentize new file mode 100755 index 000000000..34a307e1a --- /dev/null +++ b/venv/bin/pygmentize @@ -0,0 +1,8 @@ +#!/Users/u6871990/Downloads/ACCESS-NRI/access-hive-dev/access-hive.github.io/venv/bin/python3 +# -*- 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 000000000..b8a0adbbb --- /dev/null +++ b/venv/bin/python @@ -0,0 +1 @@ +python3 \ No newline at end of file diff --git a/venv/bin/python3 b/venv/bin/python3 new file mode 120000 index 000000000..1ec499c51 --- /dev/null +++ b/venv/bin/python3 @@ -0,0 +1 @@ +/Library/Frameworks/Python.framework/Versions/3.10/bin/python3 \ No newline at end of file diff --git a/venv/bin/python3.10 b/venv/bin/python3.10 new file mode 120000 index 000000000..b8a0adbbb --- /dev/null +++ b/venv/bin/python3.10 @@ -0,0 +1 @@ +python3 \ No newline at end of file diff --git a/venv/bin/watchmedo b/venv/bin/watchmedo new file mode 100755 index 000000000..38f6e20a0 --- /dev/null +++ b/venv/bin/watchmedo @@ -0,0 +1,8 @@ +#!/Users/u6871990/Downloads/ACCESS-NRI/access-hive-dev/access-hive.github.io/venv/bin/python3 +# -*- 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.10/site-packages/Babel-2.12.1.dist-info/INSTALLER b/venv/lib/python3.10/site-packages/Babel-2.12.1.dist-info/INSTALLER new file mode 100644 index 000000000..a1b589e38 --- /dev/null +++ b/venv/lib/python3.10/site-packages/Babel-2.12.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.10/site-packages/Babel-2.12.1.dist-info/LICENSE b/venv/lib/python3.10/site-packages/Babel-2.12.1.dist-info/LICENSE new file mode 100644 index 000000000..41a1e593b --- /dev/null +++ b/venv/lib/python3.10/site-packages/Babel-2.12.1.dist-info/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2013-2023 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.10/site-packages/Babel-2.12.1.dist-info/METADATA b/venv/lib/python3.10/site-packages/Babel-2.12.1.dist-info/METADATA new file mode 100644 index 000000000..e172ebc5a --- /dev/null +++ b/venv/lib/python3.10/site-packages/Babel-2.12.1.dist-info/METADATA @@ -0,0 +1,32 @@ +Metadata-Version: 2.1 +Name: Babel +Version: 2.12.1 +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 +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.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 :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Requires-Python: >=3.7 +License-File: LICENSE +Requires-Dist: pytz (>=2015.7) ; python_version < "3.9" + +A collection of tools for internationalizing Python applications. diff --git a/venv/lib/python3.10/site-packages/Babel-2.12.1.dist-info/RECORD b/venv/lib/python3.10/site-packages/Babel-2.12.1.dist-info/RECORD new file mode 100644 index 000000000..1facca189 --- /dev/null +++ b/venv/lib/python3.10/site-packages/Babel-2.12.1.dist-info/RECORD @@ -0,0 +1,886 @@ +../../../bin/pybabel,sha256=I3aRN3AUKR9IpmmaGt4-PILLa39em5JP9bqBeDBi-yA,297 +Babel-2.12.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +Babel-2.12.1.dist-info/LICENSE,sha256=kI4K1yBTrifB0X4zQL0Uz_m_k1mS7VZSbRfJg6tuevc,1531 +Babel-2.12.1.dist-info/METADATA,sha256=_Tg4ZVo3ygCWaanpCrTmo1BAYicr_yc_1N0dKXUww-I,1340 +Babel-2.12.1.dist-info/RECORD,, +Babel-2.12.1.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92 +Babel-2.12.1.dist-info/entry_points.txt,sha256=V9FEYIY5z6b5JfCc2jeN7zdQMwNwuH7gvUrmF8OAG5I,695 +Babel-2.12.1.dist-info/top_level.txt,sha256=mQO3vNkqlcYs_xRaL5EpRIy1IRjMp4N9_vdwmiemPXo,6 +babel/__init__.py,sha256=oGMZjzNfVqY3_ZcZrsg4UqiVkHyaajV5sDi4qh74XDA,863 +babel/__pycache__/__init__.cpython-310.pyc,, +babel/__pycache__/core.cpython-310.pyc,, +babel/__pycache__/dates.cpython-310.pyc,, +babel/__pycache__/languages.cpython-310.pyc,, +babel/__pycache__/lists.cpython-310.pyc,, +babel/__pycache__/localedata.cpython-310.pyc,, +babel/__pycache__/numbers.cpython-310.pyc,, +babel/__pycache__/plural.cpython-310.pyc,, +babel/__pycache__/support.cpython-310.pyc,, +babel/__pycache__/units.cpython-310.pyc,, +babel/__pycache__/util.cpython-310.pyc,, +babel/core.py,sha256=HfNfuoCvGE2Qudn2deUENv6ClmC56IPgViKVJBeAXFA,42619 +babel/dates.py,sha256=YZrlaTLhF1p52kd4Axo2dQtrzkU3vHt_gXivgOZbJ5c,72571 +babel/global.dat,sha256=-EOoX8lXOQF0O8GMRw6Ojj5wEKQU2xLlmwmq1LE6Kgw,260169 +babel/languages.py,sha256=2LDTV9WizAuZVam0vfw-Q-QKYCQpF5DxLbmVoat7TKI,2844 +babel/lists.py,sha256=me5P-D_xBBfsQPPiWLG38A6H9BU7wwvV1KKuczXYY_8,3014 +babel/locale-data/af.dat,sha256=sVgbzj9V2lqyhT8P9l7av7IvnllunHMxeebN_bbs6Ao,197015 +babel/locale-data/af_NA.dat,sha256=9staJ5BF4By_hNQh4xkzBSxSO-Bw5SfC77gjG4C9HJo,1423 +babel/locale-data/af_ZA.dat,sha256=ZaMuNgUHOKLle56lUxFOGAAk8QM-_41nuazbqNu5kgg,608 +babel/locale-data/agq.dat,sha256=mGm4OIwxbXYyz8iroZVZoBstz-ZX0xe8kl_aqo1daiU,17248 +babel/locale-data/agq_CM.dat,sha256=AxBMSZitoHhaa6djMmFbp6iEPM8OUC04G77ZldkuHR8,609 +babel/locale-data/ak.dat,sha256=tpCFVPu4WRO3hGHb_-Cfe2CQXWtQJdcPHzu4YluXspE,15728 +babel/locale-data/ak_GH.dat,sha256=tuOpoiGUJqZ_Dlr8xlp9oIqsFL-YQrBQpDUfG5z3YQk,589 +babel/locale-data/am.dat,sha256=f3g4OSEkcFl4Vh-t9aXK9RHmvEP0FvG1HnrZuRSloAI,221817 +babel/locale-data/am_ET.dat,sha256=JHNCRHUTq_8VPokwz2uGwCZPGxUR23nJdDIzi_HFIbA,608 +babel/locale-data/ann.dat,sha256=wOaMV7Pf_1V9sJaI960wWZ0i4xZ7_vmQVhUWzS5ePuo,710 +babel/locale-data/ann_NG.dat,sha256=ewcsWP2GlH47hGKUGVeDq-cQuk-TlY2Mjqg23qBpT7M,590 +babel/locale-data/ar.dat,sha256=Q4OrhXm6pxG0UXCrsL_HnXOwArJJyCTybgiIZB56qrU,389208 +babel/locale-data/ar_001.dat,sha256=Y2HUeGZn8VV7DCPv6nXTWmkqTtzGxZxdvXYYiS5mU-o,1680 +babel/locale-data/ar_AE.dat,sha256=XId1NTigYhDpgagRw7Bgs6JFwYS5JBK-WmM9GXVGmlM,995 +babel/locale-data/ar_BH.dat,sha256=f1D15u-5clcZJvrqsnr2XShOLf6hw5hmR587r7hsKEs,651 +babel/locale-data/ar_DJ.dat,sha256=NWt7QxMxsj1-zAsJ8aXWGT1SwzKayjlbB8TxvY8P-iA,629 +babel/locale-data/ar_DZ.dat,sha256=1KdQGZH5ePC4W3nwrB1F2msNqVVnK0wYwfU-uqYAhXU,1713 +babel/locale-data/ar_EG.dat,sha256=EUDxLyjz-znR56wvAE-_IvJX-2ou_ubS3KVU83SHa5Q,688 +babel/locale-data/ar_EH.dat,sha256=OseNtW_AOGrIkKmxk2ufJXkpwOXsajJ4uyJAwUidL1g,589 +babel/locale-data/ar_ER.dat,sha256=FK8yF8SloHQoOXNpaKlnEMsS9oG0j9dhDhiSwk7euCw,610 +babel/locale-data/ar_IL.dat,sha256=TzNjvKnJXxJBRXR55WZ9yVxpHnN-2subVXqjeS7CX4I,1195 +babel/locale-data/ar_IQ.dat,sha256=G4JR6ue3O7NNz7Hy2WKKiHg9lqhaHGjZA-UFq8N9TKs,2336 +babel/locale-data/ar_JO.dat,sha256=K9WKy9urKZA7k6MIuprjN2Yu-VgySZVzK46jui9NYnY,2335 +babel/locale-data/ar_KM.dat,sha256=e0ER6Yw-J2DOL7R8JKuTBpU0_46WK0jLBKpsG56UHgk,1161 +babel/locale-data/ar_KW.dat,sha256=hsa41dETEsjs8XPCZkryiCFbPBLrv6aF5ya-ImPAT3g,651 +babel/locale-data/ar_LB.dat,sha256=b4DWnwWjHST9MvfKnu6YaGhP2ghxjpZP5TQy7w_OGtU,2336 +babel/locale-data/ar_LY.dat,sha256=rs8U7zFYfoxwD07WtkK6PO2OG3c-O6IhhPdBgwqfIZw,1648 +babel/locale-data/ar_MA.dat,sha256=ufYSdBIy96F6zJND6kKZuilh00Lpu-z3txPDPhsgAIk,2007 +babel/locale-data/ar_MR.dat,sha256=wjI22LINnrT_HVHqm1ZmJ9Ndb6bFBKvAFkeyvQ8tBi8,2173 +babel/locale-data/ar_OM.dat,sha256=gsBDBEK35j4M3ualnSdL6tFD4-u3dSbJX1NtS7znQ_E,651 +babel/locale-data/ar_PS.dat,sha256=tTTcOqje-rppgz46-DrkawXtoc9jIQ2vwyA8rRU2uDM,2273 +babel/locale-data/ar_QA.dat,sha256=QI6pzsVjeY_nxkk8iKhy_DUzfBJrZFWoL9uYKM3L9CM,651 +babel/locale-data/ar_SA.dat,sha256=yrzRCggElJ5o_4c0lC_kHaa4lvqj8ilpelA3Cn5synk,29569 +babel/locale-data/ar_SD.dat,sha256=gUbUGVDm-V2a9ViTVska833oAys0b6Au-IJcoMo5leM,651 +babel/locale-data/ar_SO.dat,sha256=fSiIj3lwo0FJU0dXbnFUNLPDvV_jejp86mHcLiGtiJ4,608 +babel/locale-data/ar_SS.dat,sha256=eAaVq86grUAnWNST_u32IrhbryyjRvUo27UG0js6zS8,631 +babel/locale-data/ar_SY.dat,sha256=4ge-CrqdFYfvu3VOdrvd7lRqhGDf2rPfTYkaNBaO4Ks,2335 +babel/locale-data/ar_TD.dat,sha256=yVONNY9PuoKVCLjT0w0qp0NLO4kShNT9j9eM_uKtQYY,589 +babel/locale-data/ar_TN.dat,sha256=J3SolHTZQ9_7HZGluj2jFu4odczgSZlOYU7spA670XY,1651 +babel/locale-data/ar_YE.dat,sha256=tsYZG7eRROrZJyzc_jUbyxbMRT157S3ocGMh7eIiqsI,651 +babel/locale-data/as.dat,sha256=yfjC3G6X3li3qbKXeVH7XgNYg8rfh8_Ynzledszs0v8,254131 +babel/locale-data/as_IN.dat,sha256=BfzVFooc90N8ufCwaNAuKzefwYLUen0-o4wUafoLYc0,631 +babel/locale-data/asa.dat,sha256=_GJJZYvYTC59PLfCA12EgRTJjDA7sSuMkb-UUhFtqpA,16046 +babel/locale-data/asa_TZ.dat,sha256=aSECPcjC7UM3Bb4Cy16Et-RHtNrhlzGM_v4zNMGsreU,590 +babel/locale-data/ast.dat,sha256=DMT0-a_yaiOt-RAP9DxJ5XJNA8mv8tRDInemAj0rsi4,215642 +babel/locale-data/ast_ES.dat,sha256=x4OX34OA3ztvfnETdf49lKg6Gz2q6Lv17Lrf0G4EZ1Y,627 +babel/locale-data/az.dat,sha256=Twvpmt6hhRFtCr5ZYamw6PoZFtuItn5fgm2q-fHWhMs,208566 +babel/locale-data/az_Cyrl.dat,sha256=NAL-u3anOfqyi3Q-Vq4dtcQQpCgqMUYKokcUlnFImu8,38778 +babel/locale-data/az_Cyrl_AZ.dat,sha256=yhmkFuYZLbGzV8Q155t3UrHn-rEqAR9LVmz1sQkKcSI,608 +babel/locale-data/az_Latn.dat,sha256=EkZYNfi4vipZ7wH0cvvd1yvqOJxwCNtYADX0SgJMnAE,2225 +babel/locale-data/az_Latn_AZ.dat,sha256=yhmkFuYZLbGzV8Q155t3UrHn-rEqAR9LVmz1sQkKcSI,608 +babel/locale-data/bas.dat,sha256=AocYvh66QWd6CTrAwX4XE3RZdXAdv4qKxS7C8GIFV4k,17037 +babel/locale-data/bas_CM.dat,sha256=NlquEbS-6vOPdnyIC4r5KRnEbj4Y0oaO56i3IeH2MmI,609 +babel/locale-data/be.dat,sha256=-h2tZAOS4ZSMT-3RhcysB3nQRoqW246t4rmypdf8LAE,300069 +babel/locale-data/be_BY.dat,sha256=0uCaBRRcbIS46dyiHK85UMi-k3b1b_uspOzBHMos2jM,608 +babel/locale-data/be_TARASK.dat,sha256=Qevl9blemNV0GByiExK_GVebgr0EEjCHcU3l5PqFruQ,101382 +babel/locale-data/bem.dat,sha256=CEpdnBsCjpGelAEVy83wV2SmV31E2r7LddukUFQH1uE,6430 +babel/locale-data/bem_ZM.dat,sha256=VbAesm4_2wMn2EmWOW7etCOWlleqpkSAjjMOtKBlEcQ,590 +babel/locale-data/bez.dat,sha256=KOIZzLg2DS9MWVx5PPS6cuj3vAjJs9GS38JCq1DAArc,16840 +babel/locale-data/bez_TZ.dat,sha256=PHbB6bmtC5Wn46cFkmE8mjwuSUTr3gCxXkmFzqpiDRQ,590 +babel/locale-data/bg.dat,sha256=tVa2E_OuDeezPg0T6YvSF7ccWaiL3Ooo33NHsRSQMUQ,251790 +babel/locale-data/bg_BG.dat,sha256=rHaVmPZT-2n7w37ORQM31-InIsuCBYpv6xWIAc0akkk,626 +babel/locale-data/bgc.dat,sha256=wVvUeGXzQqtL3zJozsd42L4y-lz1yoWALGK9xHnAA5o,2789 +babel/locale-data/bgc_IN.dat,sha256=yDVdS6nKOY7VHIlISwA9UnvR7jv821z2pgJK23eddp0,632 +babel/locale-data/bho.dat,sha256=6ki1A58n3_U02EoRmKJdXoBFRbQ9fNmiJ1fDYi7QQxE,3464 +babel/locale-data/bho_IN.dat,sha256=XOOT-VR76DArlhln4h-GrRo-SYjrqDgmhZTQBAxduFc,632 +babel/locale-data/bm.dat,sha256=Clp5DwH0y3lvvcWWeiLPJcPgNHlXCeFt3KC0y5rvVCQ,15769 +babel/locale-data/bm_ML.dat,sha256=uOp8g5jSZ0dtvQRJ_GmriD3hafnqKpY-D8VhCY0lctk,589 +babel/locale-data/bn.dat,sha256=my4Zh7NONbnC8huy6AiGCa470bu33qEKFruxREHE-eI,282760 +babel/locale-data/bn_BD.dat,sha256=J15p67iXhfwwTscvbnmaYH1jxiMf8n7kvXoaYiMDgCI,608 +babel/locale-data/bn_IN.dat,sha256=B4_pZdpAoXCPIa1M5PQCaAAjiXLpfArtM99CYm8JqJ0,5629 +babel/locale-data/bo.dat,sha256=ymKhl17iq2z3zP5e9TAHXgRJgioHmrQrb6pwGY-OhyE,22162 +babel/locale-data/bo_CN.dat,sha256=v3f48BA1za99PB5mrtwCZ1UIduBs33ehikEBdXClXWo,608 +babel/locale-data/bo_IN.dat,sha256=0wt7ibuFRWOlPVaHEUBxLluTfRQVJkFX1ncqGAzkG10,1280 +babel/locale-data/br.dat,sha256=GX69DEaR3Jyh4S0v5oE1809BKgLrg_FneM-MxXLL34A,319455 +babel/locale-data/br_FR.dat,sha256=ioO-nP2x2c5STVkiH-RvhNxxq6giVfDejh4T-FoSjF8,626 +babel/locale-data/brx.dat,sha256=KJe0Z4mjijkIvHT21ACgUPVVUEGsldy4wyq3bdWKFes,195310 +babel/locale-data/brx_IN.dat,sha256=9pIIjxmY4jmGi9LDci2mDhdHADN2oD3U53pBIRNNRc4,632 +babel/locale-data/bs.dat,sha256=kLtWwFkxJCn54PEkhRJ2VglpTfC0Zf44SiECiT4EiBs,264445 +babel/locale-data/bs_Cyrl.dat,sha256=Xe7sB73uwd3Ayd5dqB2QMHQPhwe949S5bngxGOO8gLk,229602 +babel/locale-data/bs_Cyrl_BA.dat,sha256=49_6kE_iGTwxlkieBZvxsXvVPAUrQ3hlavg2hMkUzFQ,608 +babel/locale-data/bs_Latn.dat,sha256=jWzwo3YbhqMRkMWFgGPJ1SoBrHBL5OKB6yqVoJw3cKo,1957 +babel/locale-data/bs_Latn_BA.dat,sha256=49_6kE_iGTwxlkieBZvxsXvVPAUrQ3hlavg2hMkUzFQ,608 +babel/locale-data/ca.dat,sha256=9aV1veaooAQVkYCJNiXYQTo0aNE6AbAWdqZNh3JlpLQ,225699 +babel/locale-data/ca_AD.dat,sha256=c0uaIsSk6XuXizMkzNYZcMzFcC9ItvvDTh9byxpdYV4,626 +babel/locale-data/ca_ES.dat,sha256=xAbDhTv0TIq0M66QrfejjMsbIIthq2CCXMr00ojFzoA,626 +babel/locale-data/ca_ES_VALENCIA.dat,sha256=6wYPEzHXDwDkvIbQhCTSMgpURt564FXRPFH59SyyXV4,3676 +babel/locale-data/ca_FR.dat,sha256=bm288L4_PW-Wgb5Ty-9jcQns0i9iVy4zQY8PV3IETHE,645 +babel/locale-data/ca_IT.dat,sha256=t21q_Bu_bo2FVP609O53DGAXPbAUGv5yZ-_m71S1bWU,626 +babel/locale-data/ccp.dat,sha256=VT_IPjG5KpACpsTSrIrgEO89iCiFlZcd2hl_Cy-5d5w,276261 +babel/locale-data/ccp_BD.dat,sha256=Lns8llZesFHOnqgmiYntJhMufHDSmzHOL-sYczkVxCs,609 +babel/locale-data/ccp_IN.dat,sha256=zRmYBrG8ABv6_4YB7puTs2jsWSBBgqo0yBpvsBVHxZQ,632 +babel/locale-data/ce.dat,sha256=oBZzrRCGsPkKgtlTL5tB_-J3VxclCn1GWejOU8BykyY,144875 +babel/locale-data/ce_RU.dat,sha256=rAfBzB42xX9qOXWNu1kMJ278N99ohijJoWO2XAskSkc,626 +babel/locale-data/ceb.dat,sha256=5_ihpr4tBeTK9cQGy8dirpQmx_PQn1Bf2fkkSBO2CWs,106353 +babel/locale-data/ceb_PH.dat,sha256=nQM5vO0qo0sARNlhLIXlzxBCevhReUtYPios-RIcw8c,609 +babel/locale-data/cgg.dat,sha256=fITDTLUdP6gi7b69qksuM7P0ZxoYTwaYf5_EnEIV1Pw,16091 +babel/locale-data/cgg_UG.dat,sha256=87p4kuJQ3R_CLR3yPT-oMPAT9idvdPVElVxpZC5A69s,613 +babel/locale-data/chr.dat,sha256=qw8DBaRubshOc5antI_7o6LbDlrrxjScPWo6SZA33qY,227648 +babel/locale-data/chr_US.dat,sha256=BS6w-X9I6FuixJ2upUTEdFstYRKU7FCok38SRWahHWc,627 +babel/locale-data/ckb.dat,sha256=pdz5JDJxaooNGfLSDkRp1N9srtvZV6Pgo5qgIHQ8Hqs,41867 +babel/locale-data/ckb_IQ.dat,sha256=8qfAOdEjoncef0lQt6zsXiW0Nn9uY4Fb2jQMpgJSxd0,652 +babel/locale-data/ckb_IR.dat,sha256=cNWVEpmuwNkAyJm8qdtOho5FEIB-UF2dOHSXoDIdWj0,1204 +babel/locale-data/cs.dat,sha256=O4JuNdhyU4T-sWxWLUqBqfDKQNiGwIBpCU0YZ2z3mXQ,334042 +babel/locale-data/cs_CZ.dat,sha256=IG8kxPUf2L53IheXDOJcGUHV5XPaEvrWlhznlScsZAw,626 +babel/locale-data/cv.dat,sha256=igK9P_ZU-RUqXEMr0qK6B42IiYrm2f5VJw9iiCQa-e4,95542 +babel/locale-data/cv_RU.dat,sha256=3BwI5TCAmMwZyudMpe7-AjZHPlK-sC0yXBLCMf3Mo7k,626 +babel/locale-data/cy.dat,sha256=PHPz9CehFPlXGw3ie53NRaCg_7EfK8zZnH6J5csk5pc,350688 +babel/locale-data/cy_GB.dat,sha256=ZHRJBfOpeOVn8rfsdEhIF5mY01XFhStFmTVeOmklOAk,626 +babel/locale-data/da.dat,sha256=Fx85BUtIh6nw9kfcLwmarNpRprPS0gmI56tNZMcN-tk,215781 +babel/locale-data/da_DK.dat,sha256=OZkvaI7AQcocAo2rtHtZq3d6-P4mzR0MWWOQ8EJXjSo,626 +babel/locale-data/da_GL.dat,sha256=uyqYUJOO4nd3vn92yPsEVm6mYGXWCqSUTG4DtImKc0M,589 +babel/locale-data/dav.dat,sha256=7KTLHvv2vcs90XbMfMRaFaD6jz4yj6E-3ihlc5XFonA,16160 +babel/locale-data/dav_KE.dat,sha256=FP78PK3asgspsW7QIhV0jrOHFmDG4iZiGjFBvPQF-6o,609 +babel/locale-data/de.dat,sha256=deFRtiKKuzjtfKs1STQoAJdTwj3HkRJ2Sh1c9J6JtdU,224823 +babel/locale-data/de_AT.dat,sha256=Amo95CSd2th-U2768G7EKL5gKBXGrchNMPhrP2pS0AI,2599 +babel/locale-data/de_BE.dat,sha256=cAHuCiE_b6CoIRItE8FQIpr7fR-gSsF66MtRGhU3xyk,626 +babel/locale-data/de_CH.dat,sha256=XpAGxgd0jJCk4aSBdfYbvAKqRNXGQMRRuixdqkP7cxk,4210 +babel/locale-data/de_DE.dat,sha256=uGWbXF2bkaVQcgna0h59yJQJctOj88om0v2NVVCJvPw,626 +babel/locale-data/de_IT.dat,sha256=y1GIOM88fIEDhz5M_Seiy5sk47wTF2X5nRspsQJRWXc,1619 +babel/locale-data/de_LI.dat,sha256=Gy0Iz7I3rUGzEA3ksrsiRaF3htrNIyugf8eESTuwJV8,1371 +babel/locale-data/de_LU.dat,sha256=6W2M-tlU5GBY-UrVNXBp3xtWVX2IBZUQ0cj9zgpzcDM,1040 +babel/locale-data/dje.dat,sha256=Ir17gIeE62TlN0w6v6mAMQqxdEqbxlA8WQEgpqegAek,16103 +babel/locale-data/dje_NE.dat,sha256=YRn5qozp8AlljDhoIVdw1KfDjTwJuQSR1O6zcfh6Z54,590 +babel/locale-data/doi.dat,sha256=IUjkNGqQs6I6krmdbgmYXrdmlIQcAKebSNUGGdtr4LY,24570 +babel/locale-data/doi_IN.dat,sha256=Zyy44b9A8YeoCfVJjYoKgzMxkJMJ-6FCfAAgwduAXqs,632 +babel/locale-data/dsb.dat,sha256=4lSQY8FHxdQPq7hCZ6V1BhYBqExasIA2517YcxcrUOE,242061 +babel/locale-data/dsb_DE.dat,sha256=UEem7x_iq5-p3Tpzjtja0ONC1qtHvG2MCsQMx8KvTmo,627 +babel/locale-data/dua.dat,sha256=t8qjBdqMocalXhJSPR5KH-IgaDFc42kMqQbyBf44jng,5263 +babel/locale-data/dua_CM.dat,sha256=fOhCBrVImDeIe_5GZrgYvvdY70mSsENjcTEzSld5FYs,609 +babel/locale-data/dyo.dat,sha256=-XNyXvVq6p6i8TflGkVT3G6uP4-IXAIyXniOcHy_2o0,10446 +babel/locale-data/dyo_SN.dat,sha256=coM-rzzSZhBVSz9gchqxki0QnX4zhOD-Lk1nt6sPCxE,590 +babel/locale-data/dz.dat,sha256=iAXYgyHy6pHfEK1ocK8qFbEbawwONF_VrOyHQl4eo7Q,89971 +babel/locale-data/dz_BT.dat,sha256=__K3dx97Ynlcn2gcV40OXMmd-G1YchvffwSv4old74k,608 +babel/locale-data/ebu.dat,sha256=bAUdnWf3QdyXegMS9prM58PIVPqHLogSWe-xXtgS8TU,16124 +babel/locale-data/ebu_KE.dat,sha256=8py7zy73R_NJUahomYGMJF7at0vD_TMjbQy8AT7OgO8,609 +babel/locale-data/ee.dat,sha256=aZqb3B-3-ar4852a5SPsaUCa6heiox8wUgCMFMhuRcE,147995 +babel/locale-data/ee_GH.dat,sha256=B_aqQhHklFcblsk2BJmIZgwpAX17_pZENzO1Xoo1LpU,589 +babel/locale-data/ee_TG.dat,sha256=MG63DGqUVcZLwabu4o_BezzGovDY8g30wKLHkrbEU8o,1141 +babel/locale-data/el.dat,sha256=k9qHALvoL7iWBBicO109zlqIgWVh-I6_is_INnAFcxY,265803 +babel/locale-data/el_CY.dat,sha256=07pyrGXJzOGhfxhX--19MAVJlDzuBGe1AfsnkOiHBeA,608 +babel/locale-data/el_GR.dat,sha256=WqPmX-_0xIHS9A8kTDJbVCJXhqr52NEiW0CyLdGm6yo,626 +babel/locale-data/en.dat,sha256=Ku1ta1HvKnj86SHnhi16K2ZsyrvU_H9tLt5imQej748,209066 +babel/locale-data/en_001.dat,sha256=RQk6VeedfzPedWmYX5hzfceGsGRha4MvhdXUmRppHDI,31997 +babel/locale-data/en_150.dat,sha256=fk8roPCAcfJ-j08NKndsCP1Sv5nDGnNT5K5fpMKgKrQ,1824 +babel/locale-data/en_AE.dat,sha256=_fMCUB3zJ19XIagVmyZTSJd1-tYBBRftuf7NkLjV2o4,4158 +babel/locale-data/en_AG.dat,sha256=AKSzhMbGCUU-P3phlu6HupABdKSEys-M96wBGbNwgBc,627 +babel/locale-data/en_AI.dat,sha256=6ihO9cDogLMcSHgmWHgJDTg0OCTfEC4fVftMAbv3-zo,1179 +babel/locale-data/en_AS.dat,sha256=XkSzLYRfcXzQ5S8kaps6cLh8lVLcEtpS7v5GVYJ0oxA,608 +babel/locale-data/en_AT.dat,sha256=hyZWbfR6yFaN2Ck196xrxuJIPgKPQ8il0D6kJek8fNI,1267 +babel/locale-data/en_AU.dat,sha256=62SK4YO8BU8VVNpLbEbozjfrzUbP25_L2l8itmUaSro,27154 +babel/locale-data/en_BB.dat,sha256=qYW0ov3TXlP5U49PsDwBoGuNqydFwIDvAzb9g_PU4BQ,608 +babel/locale-data/en_BE.dat,sha256=XusWtgxStVPJCvYLDwRphiW1f_irYgYqmvLT2BmgZ34,1505 +babel/locale-data/en_BI.dat,sha256=FPbgGK-AHgRjz__MFHCzzOUIGhAmhx662sqL-CZWyXk,1162 +babel/locale-data/en_BM.dat,sha256=xkpTQIMT4L5hhYXe9ANWZkxgyS2H6wsMdG1qtacDJH8,627 +babel/locale-data/en_BS.dat,sha256=zHTFOmXUvagki_mtGdj0lht3V82InuI8-9cTPhvQ8UA,811 +babel/locale-data/en_BW.dat,sha256=mkdp7htCgMvlZ5ZoXgT11mn0m7WBpH3FdELKBw7zzx8,2823 +babel/locale-data/en_BZ.dat,sha256=h_ls9ROq78VZMCYsktwVXWIt90j0JHZ0jUf-k6Advhw,3038 +babel/locale-data/en_CA.dat,sha256=DLGp64nnTxtP2Rtv-hCbmHWTsYocx6G2BuFLXi7AfvQ,42938 +babel/locale-data/en_CC.dat,sha256=n1D9R3tb0Kbc3Umv3cS_2IfDv6gXJDkauUmLRQ85stk,1160 +babel/locale-data/en_CH.dat,sha256=RaqNTYr3q0-feB3B4eDThMR_qE2W6o751NvpHEnrzBM,1824 +babel/locale-data/en_CK.dat,sha256=Te6ZAYFDOE6v9domOnOXCxMrCpBSt4y8i6xxX8zplZM,1160 +babel/locale-data/en_CM.dat,sha256=vogNfPzFKhhNfFR3TzWWE-P8wKdkeGujj--c757Iy-M,1410 +babel/locale-data/en_CX.dat,sha256=CkFYiGUksAivYRZlmH-sfvwQ8HUgJ5Iqx1LD0UXbcCg,1160 +babel/locale-data/en_CY.dat,sha256=Vl4Oa2RNsxbDmyq7cJaN9UbtK3KSvSpBQZ3ouieaDXc,608 +babel/locale-data/en_DE.dat,sha256=v6hfCSEPxldGi8xgt5PDd4lQg8DI7mNHI2yTXhw1zU8,985 +babel/locale-data/en_DG.dat,sha256=db0CYrcJAtcIl9MFAGUuzVUfUS4x-I0ppd4nNP_igGs,1141 +babel/locale-data/en_DK.dat,sha256=09098Krto_8QdzAcWi0WRLhiMFMF0R7nT3LE8VlYJ-8,2407 +babel/locale-data/en_DM.dat,sha256=d1muNBxiDWlN78TXhEJiANVe_0UxZGjJ96NoIzmPQH0,627 +babel/locale-data/en_ER.dat,sha256=ZCZFGjPQWwc95TKwRCihywYh1yj-TaRmnmJqj26-dqE,860 +babel/locale-data/en_FI.dat,sha256=4l_cRPoRnfznEacTx4w5wnogtzDbb-LPpVx9X8mvdRM,2339 +babel/locale-data/en_FJ.dat,sha256=4dotX9Otp56WxZz2vqeHMl-FVcBKiT8BlqGktxoWKFM,645 +babel/locale-data/en_FK.dat,sha256=VC91FNLl2QBhA5qkxhodFR6TWRlIus-UHJ4dzJNtebk,1183 +babel/locale-data/en_FM.dat,sha256=kwQ5xP5wBGKlO-Mb2iqvsStkTC2JfMc46gBnlFTiI3M,589 +babel/locale-data/en_GB.dat,sha256=Xi7wAygGZkaUTqB8t3ZMLNx8Lsw0GIyzOJ6WIRj8gko,23716 +babel/locale-data/en_GD.dat,sha256=MbV-yK2BeGhQSajMlcL9TvEWWmch0zGbfBFcKj5eBzs,608 +babel/locale-data/en_GG.dat,sha256=qW--gYp58HZQysSEvEs2e-PzujR6nxJw76OKtml_R9g,1246 +babel/locale-data/en_GH.dat,sha256=WfV3h6HkT4PhJVSqNqIcrYOA-eWWywIMUCk6GX2DGkc,862 +babel/locale-data/en_GI.dat,sha256=Pcy6njgp-RTvil94c1DoD8gPEJfDqJxSa6Ap3hz4hLQ,1201 +babel/locale-data/en_GM.dat,sha256=1-9El_7BaSwYnE2NvFGES1XWvUOA3h31FOz48SyWhGw,858 +babel/locale-data/en_GU.dat,sha256=Msmmrz-7nw0Mm50eo0Df8mPsJFRBBbbDtCgjNN8K4Do,688 +babel/locale-data/en_GY.dat,sha256=U8FjrK6RICyHeGXrXRziQ-x5MYJv-67aiNk870rRU3U,667 +babel/locale-data/en_HK.dat,sha256=h9HlVb43-lb_Apw4_0job44AR2yuA2pGB7PLD8IU9Xo,2064 +babel/locale-data/en_IE.dat,sha256=owbfgGakzL-EetWiBetrHeMPXdKmMhKaAVzUoI7Hix4,2067 +babel/locale-data/en_IL.dat,sha256=KmYFTJlvN4yPA1h81uPevqnBJbEMySjB6BwcgvtARKY,1397 +babel/locale-data/en_IM.dat,sha256=PYHp1IMNWDea7-pfiNMq0lMSwlw6jrd1W8PijFVp_-c,1246 +babel/locale-data/en_IN.dat,sha256=304vvLzb4Q-ZcDSp5AdGyi2GtaEa435iz2ZC-i-zcho,11567 +babel/locale-data/en_IO.dat,sha256=KdFDEZ-ATOiBnVWa2p-QJovncI2Voygei_GH8ole4vM,1141 +babel/locale-data/en_JE.dat,sha256=7Cmj2eNyJOMB2YSUAkna9CGLn9cHuAMoQedkWzTJtZ8,1246 +babel/locale-data/en_JM.dat,sha256=0Iw6zJj5M1bmPLka83y-Ii5axU7oC40O2BamYcln5z0,1639 +babel/locale-data/en_KE.dat,sha256=KFU4dptHt5iSqN9zSPGv5_HkLkN8tcXdMoZZlSaK3OE,1431 +babel/locale-data/en_KI.dat,sha256=O13XFTeRaltrxnCjO4PA2NvM_dw-ye0xpJZeEnF0UAI,608 +babel/locale-data/en_KN.dat,sha256=G3xPxRBLVKbANLsnKR7b_rFGy6gYCC4YLzRI5gT8i4Y,608 +babel/locale-data/en_KY.dat,sha256=TV5QaWQr32vWrQWyBUskv1f3oq2TJVYqQPGEGJyh5lQ,792 +babel/locale-data/en_LC.dat,sha256=C_KqmNUBK_9-uE4_kYbUwNIOBeO9VJ9fpLOcaWwWDjM,608 +babel/locale-data/en_LR.dat,sha256=768u6chWYyWCDWWpSWkm8CFsSskf4e4-aayUrLDppFI,858 +babel/locale-data/en_LS.dat,sha256=K_G56Rgw6R7d6pMU5_KfwOAUvJk_hwdZe9GqU3NNfCI,858 +babel/locale-data/en_MG.dat,sha256=HA_OJPZu4eEyZP07J0MtTm8dAz4c5cXns2d5EjLArwc,1411 +babel/locale-data/en_MH.dat,sha256=lWjdFRFi5Cw9EBkpRifvGV93XeQke2MudP1tv7MXV6I,1341 +babel/locale-data/en_MO.dat,sha256=oal8-XgFkxo3F77Z5wKqP16pocMuo77-Ac9v6doamvY,803 +babel/locale-data/en_MP.dat,sha256=4ES9-ArZ1PI5CbAQ3LLDb8sLM6LVHhAnX6KgAz0VSoQ,1322 +babel/locale-data/en_MS.dat,sha256=HMWyIEh0-s1zUWHDC6XnKM8inpIDA36BSA_bN2spR0w,1160 +babel/locale-data/en_MT.dat,sha256=zougfviwyZqlZHU4AVufOGZbM89TerqGW040Sr2CWbk,1963 +babel/locale-data/en_MU.dat,sha256=Bq5ftR9nbRzJOacnOFQ7qluvifHCFAU81X4SsWWMHVM,1411 +babel/locale-data/en_MV.dat,sha256=pi7DdFWcIr0qxBJ9AOr_2N0IihzMNSRJX79gqR7q300,2007 +babel/locale-data/en_MW.dat,sha256=1-D7UAzwljnuUlgPKs2HNP0ubNQ9HGEKgIUdpkxwc4Y,859 +babel/locale-data/en_MY.dat,sha256=koZHcYmaYIjYT6OANOlHdFfPuF-RmF5iyjVbkbtb1pg,689 +babel/locale-data/en_NA.dat,sha256=384TeL01HX5bShF-vJgFfy5m65jRjC_SfITw9K852BI,858 +babel/locale-data/en_NF.dat,sha256=rEdi2JCWTfieeeS2G0OCnKMblzSSc6NsoiEg0-JO-3c,1160 +babel/locale-data/en_NG.dat,sha256=KnyRrrpnzpV97teswZmDpq3eolhm_geKohcIrryBZEA,1412 +babel/locale-data/en_NL.dat,sha256=QCLHaOSc5di3zXcLcuydSPLg4mA4JV9ghfwvifq2RvM,1150 +babel/locale-data/en_NR.dat,sha256=SVPL_wXvdKEYdWqUYhkRrgwOMc-f0YP1Uaznxqv4NP4,1160 +babel/locale-data/en_NU.dat,sha256=0cg8LGLPIboWlBVxtmd4c10rEjqPvUUz2tyyi7kUksY,1160 +babel/locale-data/en_NZ.dat,sha256=C6U6AcnLByk9BQJJ6tgyIPOFnrDlBAZbStYSQxN3to4,2259 +babel/locale-data/en_PG.dat,sha256=Cq0NrSqmEseFhwCIo6noFliCfKnx3wAOenRn3VkED_Y,608 +babel/locale-data/en_PH.dat,sha256=W7ezPkuNS7JqciskJ3G25Ic0SbHZTmmmmenv0a39NgI,629 +babel/locale-data/en_PK.dat,sha256=UjiffoQuzoicWzGR1EK7KNTDwW5OqBRiX6Ycenlv-ug,2047 +babel/locale-data/en_PN.dat,sha256=zxKpA6olu6dMYMtZpzaq35mSoMKh6AttZc6wSprPtxM,1160 +babel/locale-data/en_PR.dat,sha256=GbsBjcumdJ8L-rzRYD1cXU2uzmIUYHQX4chTgkJw02Q,608 +babel/locale-data/en_PW.dat,sha256=LH6T7NOgz_1iwCBhMne8ZH2hjPh-JHL2MOY3xktPyho,773 +babel/locale-data/en_RW.dat,sha256=RdqSwsBE4s_LG92OJvPPTxK3BoC-qzltS8PFWM2xogQ,1411 +babel/locale-data/en_SB.dat,sha256=cW7aw5w5Wos4_O_aRX1Xj4IXuEIq7eQpF50vnCEHKjw,608 +babel/locale-data/en_SC.dat,sha256=uVgNnrmBfJL7Jlv_wpfDtbdk5yFjpsTeyCTBKhiSmig,1161 +babel/locale-data/en_SD.dat,sha256=5JQaakikEVwEQ0YJm2AdZ2Zgg44mDPfl3ZKEatwChCI,901 +babel/locale-data/en_SE.dat,sha256=docfZdy5hnfCJBQ73jZogjMfFHUdqACOMqEHUG1-pqI,1460 +babel/locale-data/en_SG.dat,sha256=CU-zp0U5nRgkeyHyu3L1ls4NJJak7pmJhTBp2Osmwrw,2069 +babel/locale-data/en_SH.dat,sha256=slAAeHdppQ8lHrVY8Pat5VFVwP-imbX9RbClTrFJkbE,1183 +babel/locale-data/en_SI.dat,sha256=2uVRvVUDQF6M0oeKNQ-RQC3r023NBzK7jC5na6QIUfI,1004 +babel/locale-data/en_SL.dat,sha256=Ht0GasacEj92hUUTsR0CL6dtcfe8beK1jsPRxyU1ILo,859 +babel/locale-data/en_SS.dat,sha256=2e53Ov3bAoJClI2KxnghO_q68wsvBYm5y69cFpvZpGM,881 +babel/locale-data/en_SX.dat,sha256=Ldsv42f1G7kgTFRcGdbyL_RnXUj2_whkfivt9xCS9oQ,1163 +babel/locale-data/en_SZ.dat,sha256=qidm3zACYSmI6TgdvkJ-URbDk7BdHg1JmENh3jFUsm8,858 +babel/locale-data/en_TC.dat,sha256=BqCmasVKStg1Mia681u6ZqtglR5TxC0QgCD2j1XqAwM,589 +babel/locale-data/en_TK.dat,sha256=KmgyiXJLdOlswDEemXHOLEuZb5de5ZM0YmdDxNnAHog,1160 +babel/locale-data/en_TO.dat,sha256=wOZyazP1wVbQhv9Y_H_NDHb0ldHsMPdZPN8O-O1c5ZE,609 +babel/locale-data/en_TT.dat,sha256=UwplYXlbOs4hLPyDovdYDv6yz8KGChSZ6ufJ5htjfQo,627 +babel/locale-data/en_TV.dat,sha256=Z_vPwZ0HZB4aDDibrbzesmYFzgKRqk22hS2ZKqhq3_E,1160 +babel/locale-data/en_TZ.dat,sha256=syqDYFfemhw375IYXAM-F03S4gxAe7EbaJcYVbjt834,1412 +babel/locale-data/en_UG.dat,sha256=yczBoonl1zmDZpeNyAHAKvQ_OArvhP7AWVLOtKv9Jkg,1435 +babel/locale-data/en_UM.dat,sha256=QpePixV3RZ9RiqrYuz49bkN6Xeg-UG2y0Po_yaLbSOQ,626 +babel/locale-data/en_US.dat,sha256=JU7XRlKyRBNlDNbGDabuIBWP_tfuFahFBUNDL16cE8I,626 +babel/locale-data/en_US_POSIX.dat,sha256=3T9hB0xi9COJVMG08Gn8JRFDB2xaJYUZdUxZiOsSfT4,1279 +babel/locale-data/en_VC.dat,sha256=udrNbZKYSjk5vRAlIMd_k7255C5GUjBmQjOVm_GSshk,608 +babel/locale-data/en_VG.dat,sha256=_MFcYRjyNuFMVYGYjGBQMC3C2_IZjcSXGLxNFUt15z4,589 +babel/locale-data/en_VI.dat,sha256=ptodXPLBh9jA4d91bhhHarqlw8t0BuiigzyLPxAX3Vw,626 +babel/locale-data/en_VU.dat,sha256=OKNpgxA_p9zCpKhmDA-r2qAUHQmeEY-ITSvz6Hqlp8U,609 +babel/locale-data/en_WS.dat,sha256=_qLMqdSB0O18FukP062U6fiMk7uFaNUp-u8qjJXB3SU,629 +babel/locale-data/en_ZA.dat,sha256=pIlHaobS4oS2vrpUqX84ptZzB98_-6LJKLdcuBGsREU,3357 +babel/locale-data/en_ZM.dat,sha256=Zphtlz3AeWJ4xZaeDtX29_xuSN-aHrmFX8-dg4j9ePs,858 +babel/locale-data/en_ZW.dat,sha256=iPD_f0a9qZUtw6q7NIn1c-oa3PpceNPqkYXIrLG1MCE,3287 +babel/locale-data/eo.dat,sha256=nN0mC2tqMGHjHgcUK_Q-SafOvoWt7H56AUI-elQLH94,81019 +babel/locale-data/eo_001.dat,sha256=Mlc_rMI2tpO1RL6ZJcuMDcO5ou3NuFOxZ16TomDvwrs,823 +babel/locale-data/es.dat,sha256=8McfO7LKmKIBdKTzL-MeM2lP0pn0b7sMtsuhbIGsYFY,224732 +babel/locale-data/es_419.dat,sha256=pq16b4mIbH9xbOJsUV4X4eWn9FXabnX3AtL9aTiHN6s,30118 +babel/locale-data/es_AR.dat,sha256=7tpaCeiL8CUk3O5esNsLti8FOwhHuCAElQ27HUWpvcA,7886 +babel/locale-data/es_BO.dat,sha256=MvLKTzGXdAud9NcEr8vDat2gEsoD_IhZT8xKGawQbgo,1591 +babel/locale-data/es_BR.dat,sha256=CQBretr3RreqaOiCUo5-cmR3EIlSUiQVJMd_fhPktIw,628 +babel/locale-data/es_BZ.dat,sha256=whnvRybQayDyZH7OFfVQHMR3aHYLZhpDU2gY-j_PbYo,627 +babel/locale-data/es_CL.dat,sha256=8GpTGhxJG1Tq_8Ayj-_jUOqBpc06lvkYEP5KQzQH9UM,4997 +babel/locale-data/es_CO.dat,sha256=8GhX6FjBsC2PtUnBtdoTegrXphcoXs7jJkY3-LiyrH8,8118 +babel/locale-data/es_CR.dat,sha256=ZCVLrW4dvJ7oFoEsKJ-tBB83N3BEt7LxBgfBlzSwbTk,1215 +babel/locale-data/es_CU.dat,sha256=hjouuq52dJG7NJR3ypl_nAHpBIOYgV88AYWnR_i0378,629 +babel/locale-data/es_DO.dat,sha256=XVPP4NQZUKUHcuGstWQrOkDSUfTUBanYTX9IVMAewYo,3482 +babel/locale-data/es_EA.dat,sha256=vYi5_0kkB6gIqCXTdtHnD755-gzO2y_Gd-lAy8ahpMU,589 +babel/locale-data/es_EC.dat,sha256=LuVX8W_iHGuvulTuhrspFwwv5oHyFuwCQdVFeDSuQAQ,3055 +babel/locale-data/es_ES.dat,sha256=VCWxAz9w1EHetI06vwya_gkk7vDXGGSXJumViKKb4Ww,626 +babel/locale-data/es_GQ.dat,sha256=UN8mrsv6CDtZ3TBbiTwHFjn5YJndJjBwxugkV2R6f5A,611 +babel/locale-data/es_GT.dat,sha256=b2I6WKIGggyIJDVIxxDo-Redfk6oKToDlM3RRRboZ2c,4328 +babel/locale-data/es_HN.dat,sha256=CFMSWzpHonAw0KPpY8d6LpuH2Ly1qGXeltukAMzxV9s,3180 +babel/locale-data/es_IC.dat,sha256=ap4rHt9hZKqaWzlIZXgs92V3EWTmkCULMIY6Hf91T3k,589 +babel/locale-data/es_MX.dat,sha256=c7IVejAgTPtva5R6_bdzjF3K8KotHLnEVVi3ZkaUKuY,27815 +babel/locale-data/es_NI.dat,sha256=SbZU4zW1Xz7KfOvSq0MJS-GjIRzhl3WPfkCDzFYP1u8,1149 +babel/locale-data/es_PA.dat,sha256=Ohd58JsytquJA2FjjjnOIF3i0aOrHGclXaK4-ZOMDW0,3458 +babel/locale-data/es_PE.dat,sha256=s7M0cD37QEH9_xb2C-JkYWTaqBEcygrB_pHvTyd11pI,7373 +babel/locale-data/es_PH.dat,sha256=ryJn617ZvA3964dxe-8WFokgsPuQKJ-Q1MbwCLQ3Fko,1221 +babel/locale-data/es_PR.dat,sha256=jHtw_t9PRmNTx1l7RFOEzgQb2hJrVeZ1Tmypz_vCjRc,3485 +babel/locale-data/es_PY.dat,sha256=3OXI7u6yhD5lKPDQ4mtxQ5I_OxKoo2hSGm-hnShSZo8,5166 +babel/locale-data/es_SV.dat,sha256=fOOFFYdt6LIsNyJgLOEQdrXBTSfO11ThqwxApRh69wA,898 +babel/locale-data/es_US.dat,sha256=GQD4psFDJ0bT0lnLHL1j_MscWUpcjVVD3kDHAgCuFFk,22889 +babel/locale-data/es_UY.dat,sha256=Du8233PA5mP2YEWl4Uo8GJ9apn8B3Dq3uo55PlCiJFE,2296 +babel/locale-data/es_VE.dat,sha256=wYZBd5KzgOYLDllnsY9-KNnIDnaoYtdqASngkQaMKlA,3374 +babel/locale-data/et.dat,sha256=BszlxIJNQnW-a2GsRKobkvMle3FW1LhC_A8haj0EHyg,216816 +babel/locale-data/et_EE.dat,sha256=xpoZTzSn447SsQklqYnKUUL27DkZuNsRpQxTn87Tmlc,626 +babel/locale-data/eu.dat,sha256=P3i8oD_02OsG0I9KSF746XwZxb6Emiy95O1XttiCYJk,225920 +babel/locale-data/eu_ES.dat,sha256=xvTtCQUjEml-wPLAZR4kU_hhXZ-j5oIE5MO577tCdFg,626 +babel/locale-data/ewo.dat,sha256=-oR8kJvVwheYPNc7LXH5hMhEXaDe4ASrSODejMqIHnw,17500 +babel/locale-data/ewo_CM.dat,sha256=NirWcwhJ0SSdkIex0afT1IDuwyfJBBcfX7CGnJNIOAQ,609 +babel/locale-data/fa.dat,sha256=-cHPI7Wstw2G3kgQujgmVfi7rTdbwx7JDQ5jSniCcEM,239171 +babel/locale-data/fa_AF.dat,sha256=vVhetVdKmJLaO2TyMFN9QouU7RjT-j67WW1uu8DofAM,10670 +babel/locale-data/fa_IR.dat,sha256=ZnDClkeVc1IPiEGa63b7BhvnhklUhgige3sTjeEK6mU,651 +babel/locale-data/ff.dat,sha256=C9rRpGv49-pQvMvEOv3z4rvFawRCwh1Un86SyJjsJ1Y,15939 +babel/locale-data/ff_Adlm.dat,sha256=aOK1Uh79a-jukzF39iZbT9VE9I_Kx1yaWlX3hD4DfAk,341327 +babel/locale-data/ff_Adlm_BF.dat,sha256=7b8PdK1LA0V-odNH3iwuNDDR1vQYQhXvHp-5bB5ZwLc,610 +babel/locale-data/ff_Adlm_CM.dat,sha256=32kFf1KDw82I2SKzaVB4P8dBfmkw_mmG6fYAuThS99g,629 +babel/locale-data/ff_Adlm_GH.dat,sha256=90UIh5AUwO8eqvY2d7MzCmPwJ2XNFfAMfHqqEr-QZio,1209 +babel/locale-data/ff_Adlm_GM.dat,sha256=NqlOMO7KDanw-Z-dnG4jSX1SUESFQrNG1MVCMutQs0w,1205 +babel/locale-data/ff_Adlm_GN.dat,sha256=VAK9og8mz1WVXD9RnykNOiKkC69nIF_gGgkwKensMX0,589 +babel/locale-data/ff_Adlm_GW.dat,sha256=_BVL7y6irTvBSRhVMmICwv4uNllP5nxIqPGpU5i9sCs,610 +babel/locale-data/ff_Adlm_LR.dat,sha256=UYThYdKlKFe4XX52x7WO2xmkiHA9heV9E3K2Mg0RP6o,1205 +babel/locale-data/ff_Adlm_MR.dat,sha256=anYa5CmU8BiiYRz2nL12UDCwLJIsUIbZqajTFSYmvd8,1206 +babel/locale-data/ff_Adlm_NE.dat,sha256=EmZR_KWVdW7b5TxkRsivHLoYKwHU029v-R0k7zieWQs,610 +babel/locale-data/ff_Adlm_NG.dat,sha256=OLPxRiTM2InmMtH2gCRJRhbmwhawtdSR--6001ckT5k,631 +babel/locale-data/ff_Adlm_SL.dat,sha256=jNon1Gyb2bs6PNMY_ZhbvyA9BbmYk-7kpNCc7NebXmQ,1206 +babel/locale-data/ff_Adlm_SN.dat,sha256=9USLkiIrnIVKikQHcPqyF0bwUqc4OiAm9vDisk9boyA,610 +babel/locale-data/ff_Latn.dat,sha256=byAYS1KDI0NXZf0r5uEtjiW_dvH3S7nniynJX6jR30w,839 +babel/locale-data/ff_Latn_BF.dat,sha256=NNCmS9PhIhnRzZlE6Zn7Sjt560T_zY0oAGvs-wkJQjo,589 +babel/locale-data/ff_Latn_CM.dat,sha256=-vhCSM41OmNfJwpW2ArLlVSOBAmbxI4qkdGrOuG7jxw,608 +babel/locale-data/ff_Latn_GH.dat,sha256=OHvS5DG_Aw2lEabjE90vUXuwgumYdSdrkYU4Wz0btBE,1204 +babel/locale-data/ff_Latn_GM.dat,sha256=ISo3BU3QNGC0jjjNlgj9lfpPBE-RKcRzpWCn4Wg7Fww,1200 +babel/locale-data/ff_Latn_GN.dat,sha256=cdoXME19UJX1H7WkepaiJEiUql4zOY7h5uO8GKQoZ_4,609 +babel/locale-data/ff_Latn_GW.dat,sha256=lIrg2frFHCvM8guhuR5OmGU9Np_yUTIcORKQITZSFYs,589 +babel/locale-data/ff_Latn_LR.dat,sha256=zVPtbYZtNSl8KdW1edv19BmcMyyJ2hBA4_toOMo9xG8,1200 +babel/locale-data/ff_Latn_MR.dat,sha256=_BulUSi5RdFwiQLaD_FEaEYqfKfzVlbI2nZkyW4sUB4,1201 +babel/locale-data/ff_Latn_NE.dat,sha256=vYqMUR9LCykf0H_rTE_oeS9fYK7t-ajKpbK1IpF9-Cs,589 +babel/locale-data/ff_Latn_NG.dat,sha256=NAMpFyNWE3dSzIwJTRBwH2SUhoJlu_AzinAtCByfyJA,610 +babel/locale-data/ff_Latn_SL.dat,sha256=woELh-cKB4x9tq1mCRPltLRXK1B5fGYLHh2Dbj_bobA,1201 +babel/locale-data/ff_Latn_SN.dat,sha256=Idf9JfDjAfWlKouD9gYB6m6_qg9P9xUIRAgMQ5O1-Lg,589 +babel/locale-data/fi.dat,sha256=43Kk5Z-cv48HWbdRDlmD2TVn_m4GtBilfdrdBfLDJlA,245604 +babel/locale-data/fi_FI.dat,sha256=CqHzlsNe9s14gZWMaUn9abl4FmLAZknXRX1lP5Pdrc4,626 +babel/locale-data/fil.dat,sha256=ish7molhaPNx6bPmOkOOBkph2DrC1k9dTrxb9lWdrbM,196499 +babel/locale-data/fil_PH.dat,sha256=U-tLxLn0lDKKpUqEQPLTumOuJOIYi80HvPnUk2SsObY,609 +babel/locale-data/fo.dat,sha256=XckcnBRK6rSrr-6fCez6GptD0TtRuU7EIWBGpNH4oBM,177605 +babel/locale-data/fo_DK.dat,sha256=V7Kq03gQkns2EDztSyIiRLr80EtZsGZnmoYPsChW__w,647 +babel/locale-data/fo_FO.dat,sha256=WZJB7n6uQpGsPNWVXqP851OGZd5RmfSMbQ-s_C-00tQ,626 +babel/locale-data/fr.dat,sha256=uG8T3IWNP6SqIrArWZwOPobbEKyqmW5F4gOOD32Lzu0,242797 +babel/locale-data/fr_BE.dat,sha256=LBm5IHqOkcci5mfpTBX353G1loIeGSLvM2cNeTf-Rng,1231 +babel/locale-data/fr_BF.dat,sha256=gVdej2f-lrFMVDjQNCma1-odtbYzezlFw8YR16ZuMas,589 +babel/locale-data/fr_BI.dat,sha256=hCmnp8GizMpXqkYPSnRFUMb1ltL9xT1aIHUOt8uzR5s,610 +babel/locale-data/fr_BJ.dat,sha256=CZgeRt0F7zcnCbuwouXBxTg1Ht6M4UpS1JYNgdnGZOk,589 +babel/locale-data/fr_BL.dat,sha256=mN3e240_oM-t97i3jZ33ptBFR3XJFtq2519QXQskeDw,589 +babel/locale-data/fr_CA.dat,sha256=T6QtCKCDUWkPL3VVDBWXJY6iL24AzQEFEXHY2566rLw,73881 +babel/locale-data/fr_CD.dat,sha256=FJeps7nH-v54zvLTXGD2GjY59aPL-SAjuS_FN3s_VaE,1110 +babel/locale-data/fr_CF.dat,sha256=zElh_1aCiSapkL83eytl19hKu0R6lrE3xmb_a2lf_cM,589 +babel/locale-data/fr_CG.dat,sha256=XqZxi9XodrhYnQqagiCv8zc51Aqv8S_E3AKgZxPO6QA,589 +babel/locale-data/fr_CH.dat,sha256=-eaI2eEPrdDqP6s-QxgPYyZzDBr6dXdVXdcrt73K9Yw,3051 +babel/locale-data/fr_CI.dat,sha256=PULA-d30mWwNN2Zsg_58tbUde8ljeA7ej6_bQSvyngM,589 +babel/locale-data/fr_CM.dat,sha256=IVe0VpFrYWMJui4JKST-vNgRuRn936x0Ex01OdFNN_I,2123 +babel/locale-data/fr_DJ.dat,sha256=wDFvv4kt4rW0Yl1loIrVt5AO8kZ8zRvt-sQK4sOiNDA,1221 +babel/locale-data/fr_DZ.dat,sha256=y4uEnNW4XHE1DOLEuvBcDIgs2ei40TI7N4GOEOfThAU,1263 +babel/locale-data/fr_FR.dat,sha256=oucSQVTi8gnvWNQ07WJPnYh1YQRxbYR6afhy8Wd2YgI,626 +babel/locale-data/fr_GA.dat,sha256=hjGGeVpmPCTxP7LEMxE_iUUS-uSfRnY3unJ-800ilGk,589 +babel/locale-data/fr_GF.dat,sha256=FwIBhmnYvA-VIAgc_n9JLiENGTZMXFANqyFFpeNjNYc,692 +babel/locale-data/fr_GN.dat,sha256=BIJ_Gl1Yp5fVwQNISO_f4o5U3vgOWPKB-4UWMjp_SMw,609 +babel/locale-data/fr_GP.dat,sha256=7IjXNU_xYD73C0EaZ2IWMxZ8kzIFRLWgrE07-xHFB8s,626 +babel/locale-data/fr_GQ.dat,sha256=zMNFOsgv_5JFDvnqB6AovINlxEdr_QYBGw2Rl6LsdGM,589 +babel/locale-data/fr_HT.dat,sha256=FIn4weL4_b_phmnIIQFDdwhqY3UFaLITSGKAZh_sIJw,1873 +babel/locale-data/fr_KM.dat,sha256=SaUGzyArQSXa_pwsb9hw0_fs1TjcZq7o2CFW1mAfvQk,609 +babel/locale-data/fr_LU.dat,sha256=OZ6lHBdT7fHpiMgMaIEJhNB4ohZVMZRQiJQT98n2gLE,687 +babel/locale-data/fr_MA.dat,sha256=7-FeaIFIZGfbunjR-M-lTr0WkTGljmC354Iadk3_S-I,1277 +babel/locale-data/fr_MC.dat,sha256=se81gvlIKgey2DgfCYayuXiwV0Wykw-QM4otwduegCQ,626 +babel/locale-data/fr_MF.dat,sha256=asOP0aSNk9sx2Jx3R5QigjvOQmgEzRP0atpznWTZEII,589 +babel/locale-data/fr_MG.dat,sha256=GL58hdcr_DZePturTSmv-8WScEg60WajuKuijeBs5hQ,609 +babel/locale-data/fr_ML.dat,sha256=x_UkTI0saDvoYCiYdNF9CWoyc1VvMAQFBw8APjCEL78,1126 +babel/locale-data/fr_MQ.dat,sha256=v3tmYxQ45BkuVen2er9vMsxTceL196E98XYPsGWKXTM,626 +babel/locale-data/fr_MR.dat,sha256=BuD223TXTxBhBpwF98NgIUhXPmH5-t5fajBIsfrMV3g,1201 +babel/locale-data/fr_MU.dat,sha256=UVc2y4RDe6Jy6_48f043AXBUqWLvktTjnj2osTeAJO0,609 +babel/locale-data/fr_NC.dat,sha256=Liy4q5CQx43KEep69njgxfUENHEJRfXaZJlsK_UcIbw,589 +babel/locale-data/fr_NE.dat,sha256=beqoAaHiYhcvUeABHOBD_9cJQ01DQzo5nbAZb5JZb88,589 +babel/locale-data/fr_PF.dat,sha256=mSlv8dzrvNyo9XfC8yczKIKGaEPGTIpf71Oi1IH_f78,589 +babel/locale-data/fr_PM.dat,sha256=yukgtaee7ROFoFHM7dUO9CSYlCmipm1i5ZEIsbvvP0o,589 +babel/locale-data/fr_RE.dat,sha256=IN73Uw9cZdifS4rK4SfWiecLcAX0R2F4j1aV_DusCUQ,1142 +babel/locale-data/fr_RW.dat,sha256=b6cY_0EAjkJjlLAjdYr7o8vkdzB0sQbIgwgWsFlaO1M,609 +babel/locale-data/fr_SC.dat,sha256=ejzZtxh5_XDx1B0oZFQx7oDpuuxAsmNp1mYxGtnRs34,609 +babel/locale-data/fr_SN.dat,sha256=AzbXwg-QV0b_-M08HrFFVoi0CvQSW9tK-rNHQ-N-9d0,1277 +babel/locale-data/fr_SY.dat,sha256=0lhdk0QpQP2rwNOr_vfqUPQ-RbIXObSKTQUHqyaOyHA,1263 +babel/locale-data/fr_TD.dat,sha256=vuR_78aSnpVv6V7XSzuTrpJspe0lVWkGsQM1CrAYFl4,1181 +babel/locale-data/fr_TG.dat,sha256=GWo6BaOsi8-YZfuWYIEDMyodmtktbkK54R3fNEwvsNY,589 +babel/locale-data/fr_TN.dat,sha256=yNRxllFu4l3gX9bofg_BTz9QnLUNCMR2EL4ifPnqJCs,1201 +babel/locale-data/fr_VU.dat,sha256=FqFb4WMK4F_owTRQZXo2vlxVgi7MCG1kVz8-nPcdvCk,1201 +babel/locale-data/fr_WF.dat,sha256=_LOp9Pd-uVPzUqTOLmdKAVmqdhJSc9TQxN-q0AvFZMA,589 +babel/locale-data/fr_YT.dat,sha256=M-OawlEGCEqzxMFehDb_3Ofb76HA6nwXEBFBKEi4qMw,589 +babel/locale-data/frr.dat,sha256=L-fFl4d5BnemqGyEyW10bj3SAgPepLQZROr2IJCx8MI,53110 +babel/locale-data/frr_DE.dat,sha256=eXOAS-NGJzTZHNQWy0SiL3nFhcYG6G8UUSIiVaN_Wkg,627 +babel/locale-data/fur.dat,sha256=ibTcF2aLtp9IYYjCQEY_7bL8lKi_paYG7ZdwbWlcwbI,34861 +babel/locale-data/fur_IT.dat,sha256=-jYgvCMGoV9qmo1lNuPbfFhw2RiwM9-XrMAaisqk3ck,627 +babel/locale-data/fy.dat,sha256=o0iJeYZojO93W_NuO0gmxxerMEGuF0bIuFYQ4g4ppP8,111284 +babel/locale-data/fy_NL.dat,sha256=6kDdfEWgZuA0BeiB9BM8qhtPVPqUB4zBkpQBmEulOpU,626 +babel/locale-data/ga.dat,sha256=bnr_JdhSfUugqsDtJKUgl1llB3ApG00yk7_r_0SM90c,344177 +babel/locale-data/ga_GB.dat,sha256=DVKT5Ub0mvXWADwJua35XUCwxPrRj8olUR-xGv9x07A,626 +babel/locale-data/ga_IE.dat,sha256=cCW_n5mlSTNu6JzFj5bZMiJbEXFiOHH8BrCB4MnAi5Y,626 +babel/locale-data/gd.dat,sha256=d7E78NlA0YK0CPLMH3ZEeqxzQum6QTChnR7KwquqOtU,326085 +babel/locale-data/gd_GB.dat,sha256=6VHHQkNfDnsLrshZ5VM0AvbuOmAkVWFf6KIBK6dXxhk,626 +babel/locale-data/gl.dat,sha256=3IsUJeggybPDaWhA7YcY16irA39_L1pbtlKQwrHEou4,194805 +babel/locale-data/gl_ES.dat,sha256=taQiFoK4jse9XR19xt95xT_BXnzftMPMJgKk_ZIh1xg,626 +babel/locale-data/gsw.dat,sha256=hbEKVfQie_j4mD6y64YuoUaXviseHItAzvRkgzuOPnY,114204 +babel/locale-data/gsw_CH.dat,sha256=oNDsu5FZKmaMx0q94MsggWUHYobgGv6lNNwqRbm6mes,627 +babel/locale-data/gsw_FR.dat,sha256=4rf2w5Q1j3qezQ5Jf1-0qkY3K2Yk-ArQBBFCciWNfiU,627 +babel/locale-data/gsw_LI.dat,sha256=4aFdXjXWs0W3IE-cqe06PKFdB1l1MbQre8NY2sm3lWM,627 +babel/locale-data/gu.dat,sha256=ilqOtgibw-ewFITrEMlfSRXWBqpGCZj90Mu59RSZ6P0,265509 +babel/locale-data/gu_IN.dat,sha256=4mup-pKABihWun3Ougbz8HiGoXtPDPdAqAKMBma7Gvg,631 +babel/locale-data/guz.dat,sha256=cKJkMRMPh1_sc77izOmIha8KHGsJpHPuarv4Wukep_U,15897 +babel/locale-data/guz_KE.dat,sha256=S-xrYnojrbkIXv_AMMaLR-0Et8CtW6Ty3FDkikLYqS0,609 +babel/locale-data/gv.dat,sha256=jFC4dXw2wePKj2yU-juFCuZhXVA2yA0FKIFwE4Bv_XQ,4002 +babel/locale-data/gv_IM.dat,sha256=32eF8Qm1U-NzDs6CsC1a5G40zereETci2vy066Dq9m8,607 +babel/locale-data/ha.dat,sha256=b9EgsNapAmD91etX4AC20LVkcu10R4VAHSwcbD3_6GA,159445 +babel/locale-data/ha_GH.dat,sha256=m3n1M3hBHEFA7PWp4pgBL5ApXFo3gKTtwZCNI0m9wQU,1204 +babel/locale-data/ha_NE.dat,sha256=zijJbxFCkxERAF9QsF4GBCgaTdOa8yFdZWRAXeGL9l8,650 +babel/locale-data/ha_NG.dat,sha256=7ArPguvlMmsMd_KuhyAy5K0PTuvdzDgbCrmY5c3hyKk,589 +babel/locale-data/haw.dat,sha256=m49GeegEywvHhYURQ5QRzVQMOrfHKeLTh7EAjPKeMi8,16232 +babel/locale-data/haw_US.dat,sha256=0npKxik41EG4w134GeOKBCqQiyn4W_4RU9Xol9An9vI,627 +babel/locale-data/he.dat,sha256=nVny6iyoRSedk-rWDv3WsR3FeimwUst7a2-mqUVxckY,303440 +babel/locale-data/he_IL.dat,sha256=tv1zu6LbE2qFr5KkxegGM6sl5YjsHeOohznihTWqul4,651 +babel/locale-data/hi.dat,sha256=FwFKKGdlxmBTeo6fcRDq11Nsj9RwUDPuJYZh6wV13nc,268757 +babel/locale-data/hi_IN.dat,sha256=laF8SEGi7j2jIwdbvx9jumoN_ZSlsmM2qct5Qpdzy8g,631 +babel/locale-data/hi_Latn.dat,sha256=3MUJQMFXdeQ0ZHvht3c0U4bKL3qc1qkh1uraQGqHtSg,65752 +babel/locale-data/hi_Latn_IN.dat,sha256=laF8SEGi7j2jIwdbvx9jumoN_ZSlsmM2qct5Qpdzy8g,631 +babel/locale-data/hr.dat,sha256=0zORkbJj5gGBAfwd53E-rvlPc_3-AMveVdChaBGpCbQ,267997 +babel/locale-data/hr_BA.dat,sha256=cb0WcMYeSmL0iyadbeYGokENF3IdPgPG8Ir3pt2QWhI,1161 +babel/locale-data/hr_HR.dat,sha256=FBTFejY7tzVjCu1TCX5PtUIXfhL2iN0DukagJf7dS6E,608 +babel/locale-data/hsb.dat,sha256=jb2WlatOM5PwAlF79WeTyZxG38uiQQd75ftat7sZH-c,243022 +babel/locale-data/hsb_DE.dat,sha256=mJpofwRoSQoD4uMNdi0xcLP0qyq0IysbE2VkXNYniug,627 +babel/locale-data/hu.dat,sha256=mzeRPjbgtx-DoRmvdYGBydXCkImbSc727nRMmeB7nRQ,208704 +babel/locale-data/hu_HU.dat,sha256=KndrzgNop55GlUso27Dfpf6rW3RA7YhQibwBFTzufk4,626 +babel/locale-data/hy.dat,sha256=EnUNy3EtdR_hFrpgVvDth9pKLU3jhKOdFCXKpn4D9-o,234410 +babel/locale-data/hy_AM.dat,sha256=4HM865GP-TvuBp3XjB41rgc1QuXLLITSt3skVtB0QHA,608 +babel/locale-data/ia.dat,sha256=YEE3AMyIo0MlZwcMgrrTEzzai8tuO4mCWXl3BrNzpww,130549 +babel/locale-data/ia_001.dat,sha256=onWUTi-JeTzCyFGYj9VWyvYFnE-0w59rnsDeP3WZfCY,914 +babel/locale-data/id.dat,sha256=pM-HpFK1hqoCo1bShRw5viSgyowNLRs6n7MwAMlUK7Q,178649 +babel/locale-data/id_ID.dat,sha256=Q10Fz7etpCtqpGmEOzhzIAluNUViuPV6fJ8Ibp4TMDw,608 +babel/locale-data/ig.dat,sha256=3h4n0j_P7SGNC8pmB56qJzmH0FKvcEJYbxunerKtCqA,70575 +babel/locale-data/ig_NG.dat,sha256=qvl7ZtJLFgRf6Jbys3rPutuwKL0nImrLIyI2KxDJNMY,589 +babel/locale-data/ii.dat,sha256=LyrWW1A4eexiKIzLJo_ggvpaNmKw6Ufvc450a8X0bOs,12360 +babel/locale-data/ii_CN.dat,sha256=ff5vAOGO3nXzWVe_sQ6UFG6IdsuKvcWnOO8a9N8kMjc,608 +babel/locale-data/is.dat,sha256=NGvnffF5_8ysFKNc5ej9_VsuXgE85gLKdOPsbq8ET60,204210 +babel/locale-data/is_IS.dat,sha256=vkGTcivdc7UMK2uv1QCKnJkoGh1cFUyK877xmLKNWfQ,626 +babel/locale-data/it.dat,sha256=FE4DY2x0VHN9VTs3P9ppAQoqBj75zTFQFtj9KPBDDuE,212807 +babel/locale-data/it_CH.dat,sha256=5BStfpslfbxhW1tUWJwoZqRWxnmIFwhbvbOd7gEowQw,2900 +babel/locale-data/it_IT.dat,sha256=EPq-ZuC7fD9sVQNf7K7dGReI7pvxix55SFteRJlEheo,626 +babel/locale-data/it_SM.dat,sha256=gpwEWv5kVspUSBElJAoge82X76LK1lsgxShdk_BdxwY,626 +babel/locale-data/it_VA.dat,sha256=drgEDlw3k2saTMXzEz5-BkkHgCCdnXVQ-aiCHUMYAUk,626 +babel/locale-data/ja.dat,sha256=oJRS9TqalgGuTUH0U9n63SUlP6FUGOYdC_N2EXPWTnM,218722 +babel/locale-data/ja_JP.dat,sha256=fqV-tzCjVKeIhB1TH9R2vsz_kpEwD2KSdYUMOL2zVQY,608 +babel/locale-data/jgo.dat,sha256=_zVzqLqDWLnfdMevsp7nWExcOv2uC407_Ef37erPiRY,12547 +babel/locale-data/jgo_CM.dat,sha256=4EKGSIDydn6xezIwTpMhOZFnheOhhMWfwadqa9rRRpg,609 +babel/locale-data/jmc.dat,sha256=ym6X0J7f9pbBGaQHy0WU1cSorAtb54u1-htfim-SACs,15925 +babel/locale-data/jmc_TZ.dat,sha256=bpvlP-1bAXEnvIRsPxFHel5X-8eLxF8dUOlkJctN78k,590 +babel/locale-data/jv.dat,sha256=6acsLAIIH6lS5wGR3U1Z2PASU4jDTDr2nyZxZQUcAto,144365 +babel/locale-data/jv_ID.dat,sha256=H5wi4GL8eID9c2QUxpz6qpFn5ORgdpE2mjYxdkozJiQ,608 +babel/locale-data/ka.dat,sha256=9ZV1jIfO9SiUVEvPh92T_CHmP5xcUFwTkeLUPoCNWtE,282815 +babel/locale-data/ka_GE.dat,sha256=4G3wWIQOIZM5Z8r1Px0d4DvTOMwbR4Ubvq4expe_gY0,608 +babel/locale-data/kab.dat,sha256=lfN6hMjJdj-DGbGh_LxkY6rNFZbVJjJAi1-xGDHenQM,149013 +babel/locale-data/kab_DZ.dat,sha256=KbrMqfLO_TlWJlCQVmK5IjqCwVppZUryATx2Rq5YTaE,652 +babel/locale-data/kam.dat,sha256=WR8xJ9VZM4_i-ZYA9ZpNnnCiihUBt4Jc73SuJUkgZ_I,16062 +babel/locale-data/kam_KE.dat,sha256=vfQX-o-otm5WDwv2qrHY1lesX-AQ9cX_2HW-nO820AM,609 +babel/locale-data/kde.dat,sha256=gxDvjwKX05VCkFWccDFjMNcPLHklCbXw_QiX5SQKNuc,16356 +babel/locale-data/kde_TZ.dat,sha256=RdJ-ovdj55xBfaOc5mE41fqsNdLN_7oSIOcyq7-aApQ,590 +babel/locale-data/kea.dat,sha256=5hwDXs0QktU_8mjVbAqmq5S4eJEiMA9_m73AJcm2SG8,93203 +babel/locale-data/kea_CV.dat,sha256=7lbONkE-y9_doiZJxjyGYM50Yz41JNGwy7dV53vvhEs,590 +babel/locale-data/kgp.dat,sha256=-Gcg3Bs-bhQC45xbIjpKZAYcYXcq-ICQZRqy3s3y5B0,205354 +babel/locale-data/kgp_BR.dat,sha256=NsDwIsxDSpoycbHq2HT-Jhm8U0noCil_djiRgkRq8PQ,609 +babel/locale-data/khq.dat,sha256=c_oUgiCiIPtj2I4De3hh9-EhA3Wss2hKuEVR20-CuO4,15798 +babel/locale-data/khq_ML.dat,sha256=CbrIcKwmxw5THhW-nZ-sPFZjsfgpHZUDl-hhWH2toDQ,590 +babel/locale-data/ki.dat,sha256=PYFljnakBn7Yci8KdInk9qAc3-qrnikxyRXM1a2-faA,16004 +babel/locale-data/ki_KE.dat,sha256=-fcVNnw6zrxr3Bw7mi-vpkzP0v4v9t2hkj5ZEuG_5Zw,608 +babel/locale-data/kk.dat,sha256=ibQ3Fwu6c9U9adiFgeK1Kl2aXDsDACzlrK8sH5QmGsY,242811 +babel/locale-data/kk_KZ.dat,sha256=DhjfmmZRmr-w8q98Mqr8h_v2hosJAjTtTFo53E3QGQY,608 +babel/locale-data/kkj.dat,sha256=TvP2s91CJUYsGkKUJGMRvVmnYUrHBopGB86H87wJlAQ,4562 +babel/locale-data/kkj_CM.dat,sha256=KY8LZH7ZqifH7BTxFz4ylu4d1LAAxMAD8W-a0gYsjZo,609 +babel/locale-data/kl.dat,sha256=oIqDGEQ9ooEnsEmHKdh3MdqC8BGYj8havko6psn_25Y,59291 +babel/locale-data/kl_GL.dat,sha256=RojeiBiofKUHvk9f_ITt44uxy9gGhxz-sVy6sBn4Zcg,589 +babel/locale-data/kln.dat,sha256=QgSWBrUUgzdMjMp3ue0VV68QbOSFnI_Yl8g9L69XBQI,17890 +babel/locale-data/kln_KE.dat,sha256=RydM7LQVL9u4kqeFACUbNwf4M8vQQhP0qkKM_oL2oGM,609 +babel/locale-data/km.dat,sha256=-g5I1K7BVtOsOlA1_IEB0RDdJ2PHcbcomfu6sgst5Nw,217485 +babel/locale-data/km_KH.dat,sha256=xVjkyVxn_hrWpEp6JOzSZKxZFDZ_3UQjRQsVPvBy0CM,608 +babel/locale-data/kn.dat,sha256=enjouAVCKBfDX1k_-_1rS9HxV0sYKPZFYyRKLTPvWDk,294235 +babel/locale-data/kn_IN.dat,sha256=Kzg5Bayf-ACbA0Nun8rTGYcbi5r2PmghFxlbyQIiKV8,631 +babel/locale-data/ko.dat,sha256=KP44DmEQDxCWph4mCISPyEi4b8yu25zo4av6EZeFfu8,186908 +babel/locale-data/ko_KP.dat,sha256=2Z1Rbojo6MHJGQdInFOjfZHbpRdwvZfM-FU_08oFGng,789 +babel/locale-data/ko_KR.dat,sha256=y-3hO1aBM61NXG2L4o41zAPNlUvfA3RE14q_8SdarcM,608 +babel/locale-data/kok.dat,sha256=bWPjrV3jPg3ZVUJeO2WHOurE7VAMJLjW_k6lNhTjAmA,212298 +babel/locale-data/kok_IN.dat,sha256=e5cBMZY7kU_9H69kTorB93cUe7xFASM-2hUfGY3A-ec,632 +babel/locale-data/ks.dat,sha256=ZnQcEMYXBJKxCnewD0sO5AKT7yoTPn5Uyo9cok8UA_M,114267 +babel/locale-data/ks_Arab.dat,sha256=kfXVFhHX_NrcA7tZO6yYXym5wsDvpjma53urJPVeGJg,823 +babel/locale-data/ks_Arab_IN.dat,sha256=_fjJMmIU0OJMR66po8yq9ByOzZZ3tomRqVt6RM4BJFw,631 +babel/locale-data/ks_Deva.dat,sha256=s5caxRoYBdPp4TvaVDn0WUHF32sGoQOcAVm4dkWoIYU,14426 +babel/locale-data/ks_Deva_IN.dat,sha256=_fjJMmIU0OJMR66po8yq9ByOzZZ3tomRqVt6RM4BJFw,631 +babel/locale-data/ksb.dat,sha256=oU2e7m38AdRWc3gU0UgN_zJ0DWOnmnYLT1sf7FTLcnc,15906 +babel/locale-data/ksb_TZ.dat,sha256=2Wzocj-_i0wMu7oi3-8ynqirioha6nG9PPI1-5EMbnY,590 +babel/locale-data/ksf.dat,sha256=vKTJ0YEwumKMVfrGinmQpqcf1Fgy57_P704BoSKafnw,16394 +babel/locale-data/ksf_CM.dat,sha256=1CFxJU8F4NverN5cPa9xvVI-we8x7wbZgP3UfXVnL0o,609 +babel/locale-data/ksh.dat,sha256=JczgEekUovs196hUKmTVimYAi_-mD5e1c4R1behxV8k,90218 +babel/locale-data/ksh_DE.dat,sha256=vTdZCAi8MAGFb4wE_zjnNTREloPZHNGc38eXQ0uwtPE,627 +babel/locale-data/ku.dat,sha256=L6LL4EQd5Otsvlw1dylqdq-Bbd1LIBSRgHibOEEpMIU,28904 +babel/locale-data/ku_TR.dat,sha256=EsO9U5XN30PqoR6A-7q72uLJ6An2BMuGbrh6sYrZoFU,608 +babel/locale-data/kw.dat,sha256=mmKDRKFQFdtgt6oBk1djDlGjf128eWaM0JLoAfRnmUY,7284 +babel/locale-data/kw_GB.dat,sha256=nvzq6ku288buMZIPacs8IGm5rrD0LdzYFZQxBe9a_jw,626 +babel/locale-data/ky.dat,sha256=bYeO1jaE5fdyuW-q1o2Q8JB3q8n8dtkr51HPFj0V834,217317 +babel/locale-data/ky_KG.dat,sha256=I9WGUgCDXB09jIycutdV0trTbhHFKvbM8Cr4_eTvHmk,608 +babel/locale-data/lag.dat,sha256=nMQfiaN0hcrN8XNgim8YP0VfYeQ9QQOhvqW5BHS2mJQ,16822 +babel/locale-data/lag_TZ.dat,sha256=gB3iS13Egu-2TLYBYwM2xbve9HxMHCQwgoxELuIuxTI,590 +babel/locale-data/lb.dat,sha256=4mcQYwsG6SPRyeng90iXJCXA_uT7OklYRKkNdclsdo4,165664 +babel/locale-data/lb_LU.dat,sha256=oksbNG3kkuxhnrv6goNlYcBj0Oejpr9-ptrmWHF6EW4,626 +babel/locale-data/lg.dat,sha256=yPzj4yI0P-Mt5YWtejBoBVZISZkVV317778TVQFNqFg,16297 +babel/locale-data/lg_UG.dat,sha256=1HeWA7IllosO0gp2T_nevwD0f2CSAx9lMfQYK-JpafA,612 +babel/locale-data/lkt.dat,sha256=K0o21INPb8Q_d0GIbeGGeDG5JKL7L-EIUeOBGjt5di0,12459 +babel/locale-data/lkt_US.dat,sha256=KoED03rqibBCmXUUHPR12gR0xc9ox489Wxavkf3hJl4,627 +babel/locale-data/ln.dat,sha256=pyzay6sDbwkzEHsjhbVxFf4Fzqzsxs-_U15HuENuYZ0,25770 +babel/locale-data/ln_AO.dat,sha256=Df8fip-BEQDkkdNenJMZYVEwNEFpJU3e7TBDFk1GCFw,609 +babel/locale-data/ln_CD.dat,sha256=cya8q___2YF--XiQKag0Z2j67S_3MXvGMkqjjvao8Js,589 +babel/locale-data/ln_CF.dat,sha256=GI1_WE8tFKny1XT5c7Vdr1xpgTYtA20qoi-LbfXcNmA,589 +babel/locale-data/ln_CG.dat,sha256=gR1qJakj6apKRWJfeXchgBbbmOYiZJs-sWBiOVC4giI,589 +babel/locale-data/lo.dat,sha256=wGwWp5gAsB8fylTWlz6LBpc3nNt-9VwqKQbYv6N-0Ek,229220 +babel/locale-data/lo_LA.dat,sha256=Le3oiQxOdMmSpTmRje_U-gYUmJIgsRuIrll3v9Gvn9U,608 +babel/locale-data/lrc.dat,sha256=pICbG0n1_MMZ4cMpGiiBIL2WhUvUvJCS9cCY6HSilgM,19127 +babel/locale-data/lrc_IQ.dat,sha256=IulRzfQLGQNFx-m2GA1E-996l3AmXam6Kb2yxEU7Pzs,1228 +babel/locale-data/lrc_IR.dat,sha256=Xwe6srYtOSobQ5_3dgtaFUJPpdCzPfqdMdFw5u3h7iE,652 +babel/locale-data/lt.dat,sha256=LwdtB8KOA8cnONgQkXMSPxG4kFUW5uCOpxgsANb2khc,308452 +babel/locale-data/lt_LT.dat,sha256=xpcc0-LW9jbhEMcG4_YJ_1Zh8gjMuO_pFWRRl71WVUI,626 +babel/locale-data/lu.dat,sha256=Ww9oxo9l3kCNDotsLGhrJSky-b3FeaRJiyKY1onhtmk,15771 +babel/locale-data/lu_CD.dat,sha256=NLQ9XNdydBzo-3BIWY7FrESS7yLG1BFyU8wsX_QclOw,589 +babel/locale-data/luo.dat,sha256=tMfTl7UVUQOb_jTSSG32Pk7R0_ri8auQaFnLgsRRLiw,15748 +babel/locale-data/luo_KE.dat,sha256=NEKNpjQX9ul04z2QZGvlKaYQEpG7qpLnz0fraetUD2w,609 +babel/locale-data/luy.dat,sha256=6yMB7fLOMJ-w-dxwfJlfxjeajltDQxKMt73bVfdT7vE,15722 +babel/locale-data/luy_KE.dat,sha256=3uCT5nrrTWh8Qcd2-x0vAMbsqdBfLbVNllWdTBPXVk0,609 +babel/locale-data/lv.dat,sha256=RL8KMGuOeokpJDelSu5D57didSUP44776W5HWrjNO3o,235281 +babel/locale-data/lv_LV.dat,sha256=DVQGeBkn2bfyW4oBFSk-FG5YDgYoPrcy4P1i2msqbKw,608 +babel/locale-data/mai.dat,sha256=y7Y1wPM-ReX-bjG0D3vsymWT3fcfTJmxiZk2gkt0IGg,99156 +babel/locale-data/mai_IN.dat,sha256=lZ93VuH0KWuLZAwFYQOlGidLcq19FwAh5FcTkbmWHIQ,632 +babel/locale-data/mas.dat,sha256=uzGw-_qDWq5P1sNke_tbLXrk8cPR-uggmvTBgaqA_JM,17178 +babel/locale-data/mas_KE.dat,sha256=H37wvJs04-E8bNfKwZQhqeDajPo6SvpdVwuo3PyJ1AY,609 +babel/locale-data/mas_TZ.dat,sha256=9JwDj_IR1VKGVgWxstev2mrAXxIodBYOH4UDM6Q3q1o,611 +babel/locale-data/mdf.dat,sha256=FzzNAmNSSd8LQICO8gBw9TkinQ9HdiHYHX4YBVJr-d4,2109 +babel/locale-data/mdf_RU.dat,sha256=w_twves7EDDzHMYRpkpz-tGITmoVdJKAKEEKCFm8BWU,627 +babel/locale-data/mer.dat,sha256=kvpoAf_LZotQs9PAucYDlQmniz8ExvHi7qrxFYW2IM8,15969 +babel/locale-data/mer_KE.dat,sha256=99Q8eh6kJb1wkSHx_J0OroOC7WZ23Gp5IGAFc-NBQpc,609 +babel/locale-data/mfe.dat,sha256=HAQX4356FaKY5d5ADqf6fI_u3qUCZfpXvDf4lj9F7rY,14788 +babel/locale-data/mfe_MU.dat,sha256=TFnNwSIFyDkJUAVbw4Y2RyGH5uG4nvbKg8uNubPWXpA,590 +babel/locale-data/mg.dat,sha256=tZ9Nn8GRFCKgG3hZW2zZCVB6WztCiorOqlr2s56s-74,23668 +babel/locale-data/mg_MG.dat,sha256=cwl6h3aaMkDtvF9Ei8qvlnO4N1FTSI_VOEVa54g3eHs,589 +babel/locale-data/mgh.dat,sha256=8CZhbKywmOf7-lBmOr4zwg-hFsooKAy4noBgSNcR3q8,10164 +babel/locale-data/mgh_MZ.dat,sha256=uJyr7jkKxWqYOJ7CmhjAs8AKMOz_cWlojWjFXRj_jPc,609 +babel/locale-data/mgo.dat,sha256=uqnN6sNCGvBF8XYAZWrrBopvGSHlqNzZVbKhSJL1bTU,8124 +babel/locale-data/mgo_CM.dat,sha256=T5kZuEQ7hzI616QF05Grrv-RZb59B86medbIafdhrtU,609 +babel/locale-data/mi.dat,sha256=mb2q5M6MYEw9JdrgG2yLkj1MGOJu7I3SghjGS4tBXso,49402 +babel/locale-data/mi_NZ.dat,sha256=7o2jTlC9sR5dX2mxLI4qjVIr897Xd5keBTxs7a-_DYU,608 +babel/locale-data/mk.dat,sha256=gotYl5HfwVuPga9lQxRgcUGFhfV8GCMnogxESzsEBWc,245719 +babel/locale-data/mk_MK.dat,sha256=DtPgHruh_KrDRllM_vDipwCsbMWzk2bua0lfFsstTus,608 +babel/locale-data/ml.dat,sha256=mSDbdz97X4a7fI4534Ad8ggthOjiclLfGUQkvUVvsqk,300165 +babel/locale-data/ml_IN.dat,sha256=_vPZnTZA2VgZoDi31tfu-tR4uRzfj-cFFVMmcB8XZgI,631 +babel/locale-data/mn.dat,sha256=8xz-lzvsgjwqsFv_PunI8IYlJIXy33vt7PWhWbyT_MA,225057 +babel/locale-data/mn_MN.dat,sha256=gne5zuFemBThyeemcmnNvI751-rsRwCrCBUQ6uvuK4c,608 +babel/locale-data/mni.dat,sha256=U00kkoFwKUuH4T8ucIeZHZXe0Ue4c3SB2dH93KVrk9k,15641 +babel/locale-data/mni_Beng.dat,sha256=NiCHewI8Yl4k7ylwMAZVB7mtk6TZboLcvZl22n9uG9M,666 +babel/locale-data/mni_Beng_IN.dat,sha256=Lx0qjRdIKxv05uZIp8e9W-Onti_kqLE-bZiu4yEgCOU,632 +babel/locale-data/mr.dat,sha256=hapynpGulDrvY_UdSidkxScRy4QNZTvWJGHp_8SG-sU,267257 +babel/locale-data/mr_IN.dat,sha256=RkYd5HIWUeP8yK3zFCRWvxvu3FzaK0ta7H1HTQQMUdY,631 +babel/locale-data/ms.dat,sha256=rns-5OxYTKsqjNkKElD4ubOqN-Wb7OnRRlm2KWCnNqw,165372 +babel/locale-data/ms_BN.dat,sha256=-wJL0kyLDl_lu6yByQqJ0beS7MI0Y7yhbdqhf9dKqco,1293 +babel/locale-data/ms_ID.dat,sha256=xoDGTQo25tuchsYJ9tuKUR8snnWdEZahntfexmFUzS0,3386 +babel/locale-data/ms_MY.dat,sha256=8RsVjifl5WL8sXV_aNTdgjqquxny2SsSBXc4KqJuqlY,608 +babel/locale-data/ms_SG.dat,sha256=_nfWkz663QdJKVxb2AQQQUt_Hhl9bMk7hIQcqpVSPbU,627 +babel/locale-data/mt.dat,sha256=7GGKU1xhHoemuBR1BdoYEswH7RQ5EXF-YkamOLLNkg8,81451 +babel/locale-data/mt_MT.dat,sha256=2vQihLOtB1H_Ca6e9ZvGCgZ73gVlB_RBsBMFOw22cDs,608 +babel/locale-data/mua.dat,sha256=48UIp_NblfFH5OridfSGbPsu3kjrW8RfHru_D7oq_NU,16468 +babel/locale-data/mua_CM.dat,sha256=fQm0rv5p23ity5H_pu8jhbHVdaWDpqITuEPRev9q44I,609 +babel/locale-data/my.dat,sha256=CLhDxoto2_5gH5POMwEq2Ezu_VtQASzstl9AUag1adM,224188 +babel/locale-data/my_MM.dat,sha256=9DsxnFuIB4ImQJmOXpJ0Ujt1zMSUin-KV_a7R-irE-w,608 +babel/locale-data/mzn.dat,sha256=TsRXekYELMXyVN0P2M42GaL2pW4Xa1PgHPjuGVfVsbE,65494 +babel/locale-data/mzn_IR.dat,sha256=nw-iEKlN_b_C0VzjCY1SCElyqMgg3jQDZ4whD-lJrpg,652 +babel/locale-data/naq.dat,sha256=wCWKpZK0g1HZifWQAlM0WCApFt-E6ZxkZ2CbcDMcem0,16486 +babel/locale-data/naq_NA.dat,sha256=1Mh98XoWsJgytl-IvQuMXYJlLkYQvvoQ5_rptyu5ZME,590 +babel/locale-data/nb.dat,sha256=EqRhBsxyFRYU5_pmSZt8rhKyC_zR1NdgMRrdJ9VziiI,1303 +babel/locale-data/nb_NO.dat,sha256=bXb8K_1FTSDsqIXeZu2C0oYPCrTUpX0Y5GxKs3vXwBc,626 +babel/locale-data/nb_SJ.dat,sha256=kCfUzsB6JilDQnWsVF1CFyg-7Ewq5VOdM-EWC7PJKP4,607 +babel/locale-data/nd.dat,sha256=ujvmV_JGVJonS0uJcntaZ5bFQ7T63VSDs6OuiwNTeYc,16193 +babel/locale-data/nd_ZW.dat,sha256=szM5GcRhUeN0V1SGix3RkcgDkRNJF7D3BWJMYMOkNlY,608 +babel/locale-data/nds.dat,sha256=H3arSklBtJAFM88zWsj8kAbVOsopA8u0SNSs88sLMRs,50659 +babel/locale-data/nds_DE.dat,sha256=wQlAFyMOkjMYQd9LVFTqLFt5GuntavA1RWhBf6E3DpM,627 +babel/locale-data/nds_NL.dat,sha256=VPodVrFivmTiPf4v5OZ3Foc0_FaQwgRBuK7QiD8xmhU,627 +babel/locale-data/ne.dat,sha256=jUvazn7_86VL7qNMpFhpsBkj_-CBW2ePB-1BNmKVLQE,268081 +babel/locale-data/ne_IN.dat,sha256=Pc3G-flVbWniVZRu4RzMVYB099rVPhUvxUC1TiTCr8U,1265 +babel/locale-data/ne_NP.dat,sha256=lhhB2jPqSBwBrxK6piIkUD0YHwGUNYdlmqlPBOJhb0o,608 +babel/locale-data/nl.dat,sha256=7WAfJcvzFNSIvydSdFWiaukJaDT-ofCIb4n6Ajolsvs,230422 +babel/locale-data/nl_AW.dat,sha256=6gmsswLqSrJ0XRfJJguCU0QFSU_dTpUOlPanbq5KGpM,611 +babel/locale-data/nl_BE.dat,sha256=WOYalXajiwejntdaAmeO_f1ZWSGgetJ4LDoTroFc-JM,1883 +babel/locale-data/nl_BQ.dat,sha256=E39EYJYegrYGpAdLuSfkizgwgoBtfyRp-1Crb_I5PkI,608 +babel/locale-data/nl_CW.dat,sha256=Ho1si5eWdnrkT1_OA7ZWxarnzgfNdUmTlJLUkynzNck,611 +babel/locale-data/nl_NL.dat,sha256=kLT_7mliQl_5XhGi5lU_guTBSD6V-DUK92xhdWQxzjk,626 +babel/locale-data/nl_SR.dat,sha256=VVLYKCz48vdDn000ZzlokZnD1Qr1T7Tmn47j2wPG9fQ,669 +babel/locale-data/nl_SX.dat,sha256=FTjEPrmwtpu8IQVixzrdl0dEyAH524Ml8cWUxd0pvjE,611 +babel/locale-data/nmg.dat,sha256=hOOYbpl5DrBWlrBKa8RECAThwHgKpt16idICmteyn6A,16094 +babel/locale-data/nmg_CM.dat,sha256=4wv7ftQl9xu_DkfdjxoJ97gcm-pMhM51OCXYX3CQ6gU,609 +babel/locale-data/nn.dat,sha256=BXKL8qT-f_2zfYYrk89irMcPedQfTpZlct7CYu-hMuk,68937 +babel/locale-data/nn_NO.dat,sha256=yc4l2fwSD9fD1-sCQirXzrAkfxIqD_garBegapCzWs8,626 +babel/locale-data/nnh.dat,sha256=srVXo-_3IGaLJE0Nq102ATRloM7f0phx8KtdHQgrEoI,6619 +babel/locale-data/nnh_CM.dat,sha256=azweVaEFbSCMHLptoZQ46yKcr_antYfr2pRmxIuZQCk,609 +babel/locale-data/no.dat,sha256=dueGH7YWqe6BAKI1ETi6yK0URjSnALgSPwdf1i_KeVQ,228900 +babel/locale-data/nus.dat,sha256=0FtsK466xY4GHZyIZKRffaGKa0Ul2Y_O_ngHuSl9_MQ,9094 +babel/locale-data/nus_SS.dat,sha256=XQQtE8pKShDclBfN4yU1Rh_zEqrYFVndB-t2ScdGGUs,590 +babel/locale-data/nyn.dat,sha256=mhYes12EtEEPrKejY-RSxGj7wxNmYo6M2CjN9J9UpFA,16138 +babel/locale-data/nyn_UG.dat,sha256=i2Qcu0KO73WK35o2BvnFV9yd6dLK_p69_LtbVTMkCJA,613 +babel/locale-data/oc.dat,sha256=nN4PqUndLnoZyLeZA7P4pQ1uEM6pTeYxVBk9dOpkyA4,48138 +babel/locale-data/oc_ES.dat,sha256=pyZo4oiuTBN7SRNcw2W8mWgcujs55aopbj5Z7exvS04,50353 +babel/locale-data/oc_FR.dat,sha256=1jMBWR278mIITTibDCr6aMMvIokKV5tb7h2G2HSEEOM,626 +babel/locale-data/om.dat,sha256=v4Sz7bs1YwMS3UOjN-eHbFtq0coC8SlfYQFD8j1Gbm8,16714 +babel/locale-data/om_ET.dat,sha256=MhyQf6WK9JWcW9TuiTrQwo2C8tKIELtGKBiJ5Scrt1A,608 +babel/locale-data/om_KE.dat,sha256=A-EqNdXkq-j_TZK182yI6rnZYaFGGgAMyM76Q68FdG0,1566 +babel/locale-data/or.dat,sha256=75X2u-YLKXzJarjFZ0qgGtbFNUklGD_Po0rxjtF_3CM,260770 +babel/locale-data/or_IN.dat,sha256=tUmTnuoY49gDDU8WbdUCLyhv_2Yo-JJc_iTZlOJrH2Q,631 +babel/locale-data/os.dat,sha256=8alaKdKfdtqHn2JZKivOhCWTk_X2vadz4xNo7dxAY9g,17369 +babel/locale-data/os_GE.dat,sha256=bGm3R8Bz7k8wmb2GK_Ol83ud254rlJMbs26c1zN0w4Y,608 +babel/locale-data/os_RU.dat,sha256=A2armkX5bdC0hKe6ie0WxB1IB0exTMHAZWk_0PNjFD4,668 +babel/locale-data/pa.dat,sha256=w2w0dzS0bYMGeuvOLyjlPKUaXFR8rxLVOq8ZmxKN1Yk,265173 +babel/locale-data/pa_Arab.dat,sha256=pMo66e7EXh4jqX9idnjtof0U6BGLUUAspwuJRf38QHE,3658 +babel/locale-data/pa_Arab_PK.dat,sha256=-x9ycmOmzJ0ZIzy8J1ryO7nhRuBceY7ravgHBLEgyDY,608 +babel/locale-data/pa_Guru.dat,sha256=risWFebHubet9zREjGQ-AIrrtBdOtKXo0yafgX6FhJU,1249 +babel/locale-data/pa_Guru_IN.dat,sha256=tyUJVyrhCWckcBP10pfvLg2Xgv9shPpvWBaSiXg-G9c,631 +babel/locale-data/pcm.dat,sha256=p3JBj9jZhW5xM-M7o_7zhWTr2YrrzcPwsCcfzWs0VLo,196060 +babel/locale-data/pcm_NG.dat,sha256=E8wPtqkOJFSjTkU6T9V4RCZcer0Vc52PBvZ_LioI3S4,590 +babel/locale-data/pis.dat,sha256=g1otCnQS7c1fVbp7tOM0fHgNsRKl0eqRGl_ACuVOubQ,2026 +babel/locale-data/pis_SB.dat,sha256=Q-CrGezNS7j1SeE3H-fmeOjSlm9Q1jme5_UgbSgdpXw,590 +babel/locale-data/pl.dat,sha256=m0NWwvtbURKFDBQirk7FMkd1ubkCnBfPzhry5y9EQ2Q,264632 +babel/locale-data/pl_PL.dat,sha256=V62k9TTJ4FpN6KYLXzlsBBAMjHXnlNFuBnGoY9TBdDc,626 +babel/locale-data/ps.dat,sha256=5-asLocA5cOg6CQLpGCOBR6z5y1aeVE42C2tGCc600w,191913 +babel/locale-data/ps_AF.dat,sha256=goJChlJTUKnh7pZiMUkZiRMMth5lshKHVDZFwKq3Iwg,651 +babel/locale-data/ps_PK.dat,sha256=_MSs-UxrpD1DJvl72MouRRhRmYbE8F6-MQ6q7ansCVw,7954 +babel/locale-data/pt.dat,sha256=FcZCe3yg7WGBE8rIoeMSxpC-bLR6nZY-UEVYVEZQInM,213639 +babel/locale-data/pt_AO.dat,sha256=cTQSDeLXeH4NvRK6Kydc1LB3QyQO9qOSwh4UE7Ngga0,995 +babel/locale-data/pt_BR.dat,sha256=PoV5yebMbOhPwgtPQJ4qoKxOhQd3E5NCYcjjgOsvqu4,608 +babel/locale-data/pt_CH.dat,sha256=aFs_w4Xa1ZxFfw0GnV7IAj92XFa0xpK9mN4uY4ynDho,626 +babel/locale-data/pt_CV.dat,sha256=Yw4QSAys1d30PIGGjYLi1K-o0ME4nRnZ5xlO6mkj5KM,1012 +babel/locale-data/pt_GQ.dat,sha256=mQbJaJxvrVnC7MaTHD8r36VIe7vTfJfudKJc5XjzFg0,589 +babel/locale-data/pt_GW.dat,sha256=DTJrtZaU3wXwYHJvKkJK8BAZCcT9fSnsspJ25xuM4Po,975 +babel/locale-data/pt_LU.dat,sha256=rgoGwpHgeahu8nLnT-uyVsrkD4CrWWwTCqDct0NBJmw,645 +babel/locale-data/pt_MO.dat,sha256=b-vya6QGwLOo2Az-_AVHOv_iLSdOIn_MfvtHjiILt70,1608 +babel/locale-data/pt_MZ.dat,sha256=hmhU3Y7HgDXrsyTUootEEdjCO4dy8wxGRvZRezeWq_Y,1015 +babel/locale-data/pt_PT.dat,sha256=WwZHbYJ7c-nfmCVLNAJmY0trX9oQyWPyw1M_YCs7oi8,108281 +babel/locale-data/pt_ST.dat,sha256=YBm07Nws76viG-9eMRgf3fn-n2B0jCptD5N5s6OWySA,995 +babel/locale-data/pt_TL.dat,sha256=qG2kU_auBSaMJNnYn6fYwxspLJ3OK0EpL6Qd9-Qtzi4,975 +babel/locale-data/qu.dat,sha256=iG04WcukZ__-tuEyDor87TRCifzP-ItVnlJYePHB6C0,127149 +babel/locale-data/qu_BO.dat,sha256=CGWYNs1_AuQG3T-fYwe-q2AwDl9LAEITGRRYzc_MdKQ,836 +babel/locale-data/qu_EC.dat,sha256=WewzwnSQA5llc9gb5UYy2ue5Y8_HRb1HnddOVIXcf6Q,810 +babel/locale-data/qu_PE.dat,sha256=gT0fXlP3-rFSzIy6SdYVt-6viGPP79ukYHbBynqU4Bk,608 +babel/locale-data/raj.dat,sha256=jmfvWd8qXmu6zLNN0o8sMPVMRKQK4Sc54gkVsIxMJn0,2382 +babel/locale-data/raj_IN.dat,sha256=rtNdXBh0TKHj_SZqa0LasqbApMnH6JjoB0ErxAHdl_Q,632 +babel/locale-data/rm.dat,sha256=vBqh6BlUgV1-SyrnyS4xDi8idwrxVkeyPRp_l519Zw0,102081 +babel/locale-data/rm_CH.dat,sha256=atueKnCga8bqm2GrXOwBjQf1ypER1IAjcv4RX6Oz0Sk,626 +babel/locale-data/rn.dat,sha256=-bI9OgKokbf8IgMM3oP2hRIDwxiQNrCjNckeKc5rwrg,16690 +babel/locale-data/rn_BI.dat,sha256=II-eZWKAf73Hh0aGZifK2NLJvvXWws8a7Uv_2TUZ2VA,589 +babel/locale-data/ro.dat,sha256=4ddZMNjhfXwrumpgsdnfQDRJ0_QNxk6JrR9nMe_dLK0,253761 +babel/locale-data/ro_MD.dat,sha256=tkVS0RiHpOA2ZjCZbufnznw8aVIFj-XLoWr8whLVN3w,3215 +babel/locale-data/ro_RO.dat,sha256=rFeMpPnG0zRek72AxhPuZAuJFKAuA-UL5fAyLAnPiQ8,608 +babel/locale-data/rof.dat,sha256=cY0cJSbdQN9xc_I77-R-yS3zAKb3vdaFAOb3tBbGHQY,16033 +babel/locale-data/rof_TZ.dat,sha256=6mZ6eFqNAqwuWCZuT7oZClLSv9eWSdGH0efVoQqxj40,590 +babel/locale-data/root.dat,sha256=FPoQWneKRZ5iSw4qkvYZHgJXxBJpI_iobXugNUs_YL4,47196 +babel/locale-data/ru.dat,sha256=snr4s5L6H60jN0z6LnMLowijgHzh2PvVXvMo_2HdF94,331028 +babel/locale-data/ru_BY.dat,sha256=Pb4BHcT6RF6ONtgLhPcGQXQHVGj9dPrrodoI4ihsTSk,649 +babel/locale-data/ru_KG.dat,sha256=iQapNW3xr7lH-HEbM7CIbdQwmUjm2Tgq3iJAMFUC7zc,632 +babel/locale-data/ru_KZ.dat,sha256=OnFw_fadGFUzN0KL3WKvL0ekAwCCv5NOIhz2IFbHK0g,629 +babel/locale-data/ru_MD.dat,sha256=vZr7Dz0UZlMeWWSisvdMuyOcLyreeihFbILXdQemOXM,627 +babel/locale-data/ru_RU.dat,sha256=QhIIdAW2iPQ6LcErVIuxwvaBi1ku8V5-zsy1MZV1YU8,626 +babel/locale-data/ru_UA.dat,sha256=-TaTfE4XD2sLCYI-KHYk8lImA1jZgy2VB4TN_2JLTvc,1668 +babel/locale-data/rw.dat,sha256=LjIGfcohV-gKzJbqdq1kzd3ZzkDZ-AUkVGhvKdO2YF0,16318 +babel/locale-data/rw_RW.dat,sha256=G6ta2DtZdiILzTdyZlXTCzoL-oRoF1gekRFCmN_dEyg,589 +babel/locale-data/rwk.dat,sha256=viYAkJWmlaVDf5zroL8OfVBMHJ9NszvKaTOUcOusAs4,15920 +babel/locale-data/rwk_TZ.dat,sha256=RtQRCyT2DbqRtc4uzP8Nna10O8KouCqtbtSxCJ-PukI,590 +babel/locale-data/sa.dat,sha256=jVVSQ4-y03N8T-egnYSNVMV6wQWGBR0OpLibsnXLyns,24275 +babel/locale-data/sa_IN.dat,sha256=TnFi858nrNslv9BVw1YOhOdXLaEcLVerU0d80vDNEn8,631 +babel/locale-data/sah.dat,sha256=9vURJ5TYTA6F2ca1sWtSIeXpYzDphYR-lLOfEAZ9fJs,51323 +babel/locale-data/sah_RU.dat,sha256=-Hi7VNsxTYaC-4G8qYQsnSPVMc5jXBYQJBvd5UeC-lo,627 +babel/locale-data/saq.dat,sha256=_pUkBYP9ABjJxlLziRqoOQrS4UBTH0wKdHD3zQCr8RQ,16342 +babel/locale-data/saq_KE.dat,sha256=uHKDZR4LUK5CGvcVC-dIpdYM3uY1KXVh6vkAOnOrc-w,609 +babel/locale-data/sat.dat,sha256=NYlIpPrtO0FGh9rZZS5wHpSHgpzgTGwLHTVTnv2m-uI,69215 +babel/locale-data/sat_Olck.dat,sha256=XNqbalNvgWolPY1M9vZXPpLzFkzYzjSAvKtoP1x5oBs,878 +babel/locale-data/sat_Olck_IN.dat,sha256=Rx6KNBVSK2m0PvmKzotwxqBIp30-b5dCQU5-hqSB8tQ,632 +babel/locale-data/sbp.dat,sha256=xWxrHPxrOIBWyI15vDu_pHlU98dhBfuc8-R9-xI1sHU,16388 +babel/locale-data/sbp_TZ.dat,sha256=myr2BmLmSpSCCyRFCjm70nQfdeUAopZ29zxfemg6F8c,590 +babel/locale-data/sc.dat,sha256=rnJtT9uk29gLAnlBAYJj_LVBd4suXscyPnMRbeParsA,199105 +babel/locale-data/sc_IT.dat,sha256=mt4qFD-KvQnJaIjwufXWJyYxULQshy8_5w--RgEjDoQ,626 +babel/locale-data/sd.dat,sha256=nMoOQ5gBv2lvBdQsRm1c0xUMUU4oK0YMEXz1dbFzO_Q,205523 +babel/locale-data/sd_Arab.dat,sha256=EOWPc5-ACgE6NQEHILMBA_BP6mK35sTdUEEuEFCv748,852 +babel/locale-data/sd_Arab_PK.dat,sha256=pNtPPmwu0jQK9V31EOv-lVoFiYwf1iHDxJmB5NNIZzU,608 +babel/locale-data/sd_Deva.dat,sha256=pSY9wcb5Knj8WOy-KxMcMXGoWGtHY-_ne-TU5frjZaM,16521 +babel/locale-data/sd_Deva_IN.dat,sha256=Uei2PSaYXixwn6VPwb7xeFMXt8I_jyM_myr-8lADGVs,631 +babel/locale-data/se.dat,sha256=dZB1vNg7NmUYu4LgqN73hSRSbkU5RxO8t9cRtD8wHPU,73980 +babel/locale-data/se_FI.dat,sha256=Do3w1bzT8_fqiynWUnKVuXRJazh43HYovQfsBBzXmwE,47217 +babel/locale-data/se_NO.dat,sha256=k-BEm9_tnVXbt-H7pAtUU78dZfuiq7-cTcbsgsqISlg,626 +babel/locale-data/se_SE.dat,sha256=BxFV9gNTLfUF3ibsRvgeuRnuDo99396qMA-89tpdEFY,667 +babel/locale-data/seh.dat,sha256=rwsKF56ysgtQx4Nh18X_Xs8OyPu4WzcD3Dk-xk3Z1Lc,15769 +babel/locale-data/seh_MZ.dat,sha256=feukobIWsGC_o5s_qb0UgFI7gzVCrNSydoRaXs0NUZ0,609 +babel/locale-data/ses.dat,sha256=CwURrNdxyArjP2_vNSlc9GidQthPetAhzzp8viQzSCY,15857 +babel/locale-data/ses_ML.dat,sha256=O7stcUKOw8ZkGmgnPqSWBCxPFA3_aDIcHZGAT9yRrtw,590 +babel/locale-data/sg.dat,sha256=yZncKIajWQGDpHNjRKlZBFyCqCkLS9OWGi4H72PDYYw,16494 +babel/locale-data/sg_CF.dat,sha256=dDZMdfhJBfy2ShSVhAopU2nIEEBnTssu3Vji2v9SpHg,589 +babel/locale-data/shi.dat,sha256=yA8m8bjRvPNuY2DI2na8p_XrIn38ZJEAkiMNIFZK5_0,21895 +babel/locale-data/shi_Latn.dat,sha256=nNOoXMih6DirYEMGxsNB1xqhsP0TK51lhGcRX1WaVC8,15477 +babel/locale-data/shi_Latn_MA.dat,sha256=blTyj-JXuFz7wgjLjUC19rH4Emj7_-TOtMvBKb7qAus,590 +babel/locale-data/shi_Tfng.dat,sha256=PmToPVEqibydgF2nxMw21pujbbqf4odWn7GlEqQL2u0,947 +babel/locale-data/shi_Tfng_MA.dat,sha256=blTyj-JXuFz7wgjLjUC19rH4Emj7_-TOtMvBKb7qAus,590 +babel/locale-data/si.dat,sha256=R0wPi_nY7qKI7fG625LTSAcwYmzvYLVuc2voQ7AYJxM,266857 +babel/locale-data/si_LK.dat,sha256=2k1GulXssuQkuKMmj4J74iAYHlfh507gp6l75lKDJwg,608 +babel/locale-data/sk.dat,sha256=DhwgNg_FnUSH1qORXgOCAMWVl2_XfYTLrcMr7uj6GTc,280987 +babel/locale-data/sk_SK.dat,sha256=b8ugTdqk71Ge03FdSEhnOmH0wP5XeDSI40ys2hGovNQ,626 +babel/locale-data/sl.dat,sha256=Ss2MfA63OAgjuD4OKVPttrlWGSNiIB7wDhHJzxynufI,269820 +babel/locale-data/sl_SI.dat,sha256=V1oy5BlaUDjrX8b0jv9LK7Mgg4Yx84u-Iry4j3M8LYc,608 +babel/locale-data/smn.dat,sha256=SSsMEq4OcWhrfDSBKRtWtRmGLD3ZseJiCgdU3geGepI,43444 +babel/locale-data/smn_FI.dat,sha256=3FaHTO42uw4LsL2o9CnKHuk86tRNGaorVeYh7SPONWY,627 +babel/locale-data/sms.dat,sha256=WzHRTGLfD_SLuD2c6xZ1bFjR3oibP-2mHaejlFtfnTg,6488 +babel/locale-data/sms_FI.dat,sha256=a-XVjjPkU8UgWQiWiWEX3TnYJKSeIkNfT29ixLYSBns,627 +babel/locale-data/sn.dat,sha256=3FYPTXW4rToBe58yh8lE4TyVaDteoQn2DqFT2HZbztQ,23364 +babel/locale-data/sn_ZW.dat,sha256=R48ZM21PI5bjz154uuK-wccs9d-M9YMdiQLtX-rbG5k,608 +babel/locale-data/so.dat,sha256=EqTBG60iBXZnoOrVNsSmCbHhLgLJZmElk-k2paumiY8,171086 +babel/locale-data/so_DJ.dat,sha256=CWxbbQZ8iogPci77q4bpbWHOFBOKISwLOkqixb-TqRA,629 +babel/locale-data/so_ET.dat,sha256=JKebALALzWT58gltRAjWVKl3LqFGiy1iD-nbFFsvfZ8,628 +babel/locale-data/so_KE.dat,sha256=Ooas5zUI6mtrajAybdc2YQyFlS4RFNUxeXqT0pUQ2fo,1181 +babel/locale-data/so_SO.dat,sha256=WtbiqTAukA-EouWNpajrPLIyRqUNmh0A6bcbXQywwqc,589 +babel/locale-data/sq.dat,sha256=BVJXqN_e743eTE2clncOrrp-BHjUR25DqXHTuvP0dZI,207965 +babel/locale-data/sq_AL.dat,sha256=061xsDws549-glqPY5No20svTFqs0pO6Cn9oUtwquVA,608 +babel/locale-data/sq_MK.dat,sha256=8D6OAIV9T29UVST-ZD0zjtgYgrScEKaeZDaf8fJBk4E,1181 +babel/locale-data/sq_XK.dat,sha256=nFWTBnEjpW6ux-oMbSov3ta96NrvwhjtQ80viYXFfKY,1160 +babel/locale-data/sr.dat,sha256=JxSi4rurDPxygufZTRdapFsx4cj4YCYbJLSNKqU5bFw,300002 +babel/locale-data/sr_Cyrl.dat,sha256=PqtXXFd7yu1Bweljv2UkBHFUYVPlIFY2abO3lfl8t4Y,1957 +babel/locale-data/sr_Cyrl_BA.dat,sha256=O5nFwU7zgc7aRC1onhdASbCL76XaSViwnRod1ytH2Mo,44256 +babel/locale-data/sr_Cyrl_ME.dat,sha256=tRDuVm4xa-0u19nMBjF6Neag0ivhimqO2qE6CrNeRaI,3881 +babel/locale-data/sr_Cyrl_RS.dat,sha256=AAJ6aaa8D73J6na-RIjuqhEfLWKReHLwzYavdFaobhs,608 +babel/locale-data/sr_Cyrl_XK.dat,sha256=hXdgzDWE5gDdWBbiNBaEHTl1hjEqqtd9dmnFXPFRkfc,2756 +babel/locale-data/sr_Latn.dat,sha256=2yAeipJjthqVPqPRBW9SFy_z0cAPI9tcVvQ-FgA04rM,251406 +babel/locale-data/sr_Latn_BA.dat,sha256=gbrYWDswB-XnTYN5WMzFLfxSUlU07Md0G91z7GxlSdE,34376 +babel/locale-data/sr_Latn_ME.dat,sha256=Ouj7gX_BxAv_fcPlixWvXlNyA0s3ejIDM7T4QlTtJ4M,3250 +babel/locale-data/sr_Latn_RS.dat,sha256=AAJ6aaa8D73J6na-RIjuqhEfLWKReHLwzYavdFaobhs,608 +babel/locale-data/sr_Latn_XK.dat,sha256=bKshsVkR6h3Wsu8Tm7Ff8Um_evYxhPXWi-UOKfSk5JU,2362 +babel/locale-data/su.dat,sha256=HAMD5fKf31wId1sY8pytCwfBSiWvm3KQn7EdbkuJDmk,13316 +babel/locale-data/su_Latn.dat,sha256=AoqRqUqiJYE1G-ZRCIIhkYSRQ8s71qDefLwv70XrgZA,718 +babel/locale-data/su_Latn_ID.dat,sha256=Hi1QalxGc49vElzHunyzz1Mfc6_1KgzXkGjcj04mq8c,608 +babel/locale-data/sv.dat,sha256=RIv1363LTFVIj8Lpp1cKDkCbwcy8jOnzpDAOhVu7P4Q,237480 +babel/locale-data/sv_AX.dat,sha256=4LqqSZFfMV2iRBS5TyTdWWKfdoN_ahxotRUbyuoaX8g,626 +babel/locale-data/sv_FI.dat,sha256=i14KXwU5LSAWAly_iG6t5v_MvsMbYKRIRK5U1oZWVR0,2624 +babel/locale-data/sv_SE.dat,sha256=OtNxgFxUFAH5mYJ4yGqp_wLMmnMu9mwVnvJ57BKUOKs,626 +babel/locale-data/sw.dat,sha256=z_ffCKKDkW4if5coJAmi2PvVHjsxuFTVSDO-OnqYIJM,199985 +babel/locale-data/sw_CD.dat,sha256=X5WmidVyJRqE-Zp2jpopvgLDygbcFVZ1QblhoeQGcaY,2535 +babel/locale-data/sw_KE.dat,sha256=H6xUKk8kmKP4qtSTMk9drSIB2lBYIEcjjs0VCOJczAI,47284 +babel/locale-data/sw_TZ.dat,sha256=rRGQVQ_Vp0bQ6_KnZTZDn7YDY5HDNiIsUMFLSnKD6nA,589 +babel/locale-data/sw_UG.dat,sha256=GN6zqbWL7dor3RJWaJ7EwhMX8BIA6HVUzN7xWswPgbU,633 +babel/locale-data/ta.dat,sha256=BbFku9hG2iOh3mZcMM3ejg4fAKkcPecyqMfUdZKnjMo,296033 +babel/locale-data/ta_IN.dat,sha256=1D_ISTGx78nHVtYBoccQpH04o6CCL5dIIGRm4RWkMDg,631 +babel/locale-data/ta_LK.dat,sha256=sHPH1bp1O7mbhnp0WgIyHfKckEIATHbtNtSGQPdbVSU,1181 +babel/locale-data/ta_MY.dat,sha256=gBgYAEpdQXqeUzqDb2Xr4kkZoY7BX9E9hlA4tqPb6ts,1298 +babel/locale-data/ta_SG.dat,sha256=yQUkhxL_2BLbNZK07ksyJkeHUyXXEk48BTA9xdtPqSI,1317 +babel/locale-data/te.dat,sha256=-XRZj85I3oheGt_KxZKyVdnOyh-v1Zz2E7Q1pHVjHbI,283972 +babel/locale-data/te_IN.dat,sha256=uRakP3CRkBJKNlCxMpMDOGOi5aeiq5OiLxBbUWn2NZk,631 +babel/locale-data/teo.dat,sha256=9PB80gnhglGqbDXys03I3VyrTy3hJ_pDXjIr5hKy6FM,16558 +babel/locale-data/teo_KE.dat,sha256=yZVnSmYqZ77jAPlyuyY_yivRqbj4dwbb99MW52EJNvU,630 +babel/locale-data/teo_UG.dat,sha256=o5PkAO5zi67Lxbh5vKaOJF6qerkAFvJu3RrQ0iHlmwA,613 +babel/locale-data/tg.dat,sha256=ZDMRczt33ZzkJqDJidDMuqYYXB2hUcNkA7OTI8IFGBU,50715 +babel/locale-data/tg_TJ.dat,sha256=ge5GlIElwu5VzdLeWCCEz5A2-F9ihtOH9Ic9k5ii4wY,608 +babel/locale-data/th.dat,sha256=6qW2D5IavP9zXADx6lvPugAS8CMcwgzmOu-Q3M72Uqc,255889 +babel/locale-data/th_TH.dat,sha256=aQd6NJ_y5ObTF2koe4cI_s_mVdG0c7JfaiivpLN8M50,608 +babel/locale-data/ti.dat,sha256=1oQ4l5eCSw1bk1TZ65kH-yKhVFA5HqvvPeRKKxNcqY8,105471 +babel/locale-data/ti_ER.dat,sha256=UWHECZCp7POXzJ6tDxkFG0csM5fCwyaUzrds1udOM_g,935 +babel/locale-data/ti_ET.dat,sha256=vT6Tl0BBaMupoRD4fb3kmM_Mufx2EGuqP460HO_Bh7Q,608 +babel/locale-data/tk.dat,sha256=IdDrAPtgN9CejAGaRchktqV35ipWdeAbBn7MdD6rnR0,185418 +babel/locale-data/tk_TM.dat,sha256=06szpphDghkTBac9nMge32AzZKeQdxXeZjsXF0X1-Nk,608 +babel/locale-data/to.dat,sha256=JKtiRykykfcz0ZVMy29N-AlC4S4K6SDb1JRBMMvy4r4,180291 +babel/locale-data/to_TO.dat,sha256=UaceT8b6KsoNoQd68finqhXDLVr_4GtcGFsgTwJaYTc,589 +babel/locale-data/tok.dat,sha256=DMhhik6-uCFKycIKwVr1BmW8cz8cEH0RBWFZo6UQHVI,6024 +babel/locale-data/tok_001.dat,sha256=bieoDt_x4vgLl3MQocuwqUytJRBvV3b2f50BRCQqVUk,666 +babel/locale-data/tr.dat,sha256=jE686R_FheGeKO551cJn-xCXqohhPsfD1l0CxSo6-Ik,225193 +babel/locale-data/tr_CY.dat,sha256=WncSUtrG90gWFsVn7YbRy_nJFKBDr25W3UA3UA7XoJo,1200 +babel/locale-data/tr_TR.dat,sha256=lUyZY1ya9qqjmhdYhAkP6j4V953QWx_cC16GrVZCaYM,608 +babel/locale-data/tt.dat,sha256=rj6jsvK5A1_XskXmcw1yom1j1FR4X5bDuDwfsdjFaZ4,42269 +babel/locale-data/tt_RU.dat,sha256=MqxY1dPvSLsO7huGCYIhaTqwfGw9qHzq-oUt3VwVPyU,626 +babel/locale-data/twq.dat,sha256=5bgKnEDdPL8hfCBagKLwLGdeL3bQtdPgC5CxilK_ibA,16082 +babel/locale-data/twq_NE.dat,sha256=yv89EP--ZBgtvC_Vfy7UN37T7OktzntjGpRKfo89AW4,590 +babel/locale-data/tzm.dat,sha256=-dG0db6bpfH2F-V7Zi46UeoJaRHWXhA1c5JAgky4OYA,16012 +babel/locale-data/tzm_MA.dat,sha256=jOAK87vFm-WpZK130vSvCLE8fWxfjeyPs_G1elc5TMk,590 +babel/locale-data/ug.dat,sha256=E1jh6inCH6MhPDSH-WjfdcUzgxsIRulS5uT2UbzuFHU,129453 +babel/locale-data/ug_CN.dat,sha256=smJ1D2YZCHYeXD89a9bHHiAy6XUN217EPULgY1j0QDk,608 +babel/locale-data/uk.dat,sha256=iaUP5Q2I1nawyxb8BLQ7eNuaYWkpmrbmhj6wjezD6nw,351099 +babel/locale-data/uk_UA.dat,sha256=YE9ivxv_h3qwHTeFyWTV4gt5JaYgYdKQTLHHadAQQT8,608 +babel/locale-data/ur.dat,sha256=AuBbKYxzSukAkRZqkSgfTEuixBl6cjcnTjNixn_BvJY,217844 +babel/locale-data/ur_IN.dat,sha256=wQVX00wkPazO0nWhfYBGBtTOrK6w00S8XbP1pxUZlPE,12108 +babel/locale-data/ur_PK.dat,sha256=VXzQfSYJAIlzcDjPva4UM67jhqIwDUqAVNRGB2YPcfI,608 +babel/locale-data/uz.dat,sha256=lIxuZbqrBMCGPQOgE3ZO6_XhuZVrci6BptN_VpHU1Xw,191055 +babel/locale-data/uz_Arab.dat,sha256=c87B8S9cvSqxKHcaBkceU4GEEWT_fpn8zx7cjOztsNY,3613 +babel/locale-data/uz_Arab_AF.dat,sha256=ONnsHyim0Q-GRD6BAHPTj2Ri4aR41EB5HWhJQrKKXAU,651 +babel/locale-data/uz_Cyrl.dat,sha256=7q25OHbYGwz5BbtMvZlj_qOAKZDaPuTj6_wn2UwaB5E,99893 +babel/locale-data/uz_Cyrl_UZ.dat,sha256=D2g0Iy4gME1-ZrXDPgcs1VlFNW1FWKRD607VKgUsFwA,608 +babel/locale-data/uz_Latn.dat,sha256=wkn_uCtrZQx7Ut7_pTVXVU9X956I30Nr4seILvPnZ_o,1265 +babel/locale-data/uz_Latn_UZ.dat,sha256=D2g0Iy4gME1-ZrXDPgcs1VlFNW1FWKRD607VKgUsFwA,608 +babel/locale-data/vai.dat,sha256=H7ljmMnBlgEI8nCu8gOdRYvFxNZlzQD-Fm1f0YB_Ad4,18875 +babel/locale-data/vai_Latn.dat,sha256=RTRennEbxtunQQIy8cbDM2Hh60nuyDEUXnxEVLXjxC4,14892 +babel/locale-data/vai_Latn_LR.dat,sha256=mFG8a5AB_Cnv2lwGAVg5SxhF0lgkrS4vB3UdqB1L8Y4,590 +babel/locale-data/vai_Vaii.dat,sha256=rZi5j11eMQeE9MzTRK4Gl5EhqEy_X6o3V06k_E4ioOY,666 +babel/locale-data/vai_Vaii_LR.dat,sha256=mFG8a5AB_Cnv2lwGAVg5SxhF0lgkrS4vB3UdqB1L8Y4,590 +babel/locale-data/vi.dat,sha256=5mmgSfFGdPL4C1z2tlaFjjDlCGYB_irHxpLi3AdFsUQ,174755 +babel/locale-data/vi_VN.dat,sha256=hn8-pr09TFtQwAvAau15ETGT4bmPay2o_LGOvLA6Bsk,608 +babel/locale-data/vun.dat,sha256=AWyN2CNEE6DszZgTzM8M3ss5zyYkJW9CdoKQt3_eEUQ,15924 +babel/locale-data/vun_TZ.dat,sha256=1lrpmdkRCqdDzX4Cel249MWWRGVDmubt9OiMf6Qsrnk,590 +babel/locale-data/wae.dat,sha256=pmMkSCNe6am0CdgkjguMr-wlsNp9OFpOvihH-tDLZt8,30864 +babel/locale-data/wae_CH.dat,sha256=5fOXgR-rrWoIYbf230jds8iOj1dsbvNW2Qv2XBNaCto,627 +babel/locale-data/wo.dat,sha256=jSvLFYHtrygrAtzXNuzZEzjexJj2aV3uLGGvD89FWQ4,33105 +babel/locale-data/wo_SN.dat,sha256=ryU-hZj708JELq9ldfmRU8I2EDihWGM_6v8QQY4qRuE,589 +babel/locale-data/xh.dat,sha256=hTfakvIFKm2i2VKInt0rHpOwRODiH5Mbu_6EaftcywM,91043 +babel/locale-data/xh_ZA.dat,sha256=LPaE6z0iRUaCSVd2CeuJzy_0GfhGCs2KgtYPHqLL18I,608 +babel/locale-data/xog.dat,sha256=85N5V3duDQYQ2ADXFjRMN7hH2tbnYIQ1E8vQ8kmyfgQ,16414 +babel/locale-data/xog_UG.dat,sha256=5B_ozUekB9sXcaT_-7brqH1nfv_XEP5CnB2PG84_JlM,613 +babel/locale-data/yav.dat,sha256=I0TvJnZ19Fc25M87D-ATI9mAHjvDtx3VchzUvl5wT7U,15231 +babel/locale-data/yav_CM.dat,sha256=y9SNKPJTMwmSdGc0MO-vn7hUeK4arphzRDp2zBFYigs,609 +babel/locale-data/yi.dat,sha256=9TwnoSLuTD40FR5iZ6trDPcyh2ylrmFl-x75GZhLFxE,29985 +babel/locale-data/yi_001.dat,sha256=wdIcCz3ZcZHFJqT28rBWo4iYmRffPPVWpxod_13KIYY,885 +babel/locale-data/yo.dat,sha256=MfRZ7Iit3YLoQIspVzp05YuEdppMagPPOUf-fEvdy10,101614 +babel/locale-data/yo_BJ.dat,sha256=Pa3FLKZE2h0nVFkAFrErWftfTJoQHZ3u1xiveH93yuA,42721 +babel/locale-data/yo_NG.dat,sha256=vVCmItRDqtBEzSXYDsXGoiobciBukV84o_LpnAZRiDs,589 +babel/locale-data/yrl.dat,sha256=y3M8uOEi80lM0p3yvQEnhpdNaeqX-TlBWM1IDggyGK8,207804 +babel/locale-data/yrl_BR.dat,sha256=uG2fPhWqqf_l8cyyWlp_fw_sMFGCx-SZNaZsaoNAqqI,609 +babel/locale-data/yrl_CO.dat,sha256=7stm4SEnQGrqlniqZqkpyShm_igDesuLf49E9JE8AmM,9298 +babel/locale-data/yrl_VE.dat,sha256=hg4nTVty8CLQJTbPhlS-saewjGjvy6_ORMTFEBdjcV0,9298 +babel/locale-data/yue.dat,sha256=g6wz78hxhsqVT9ZTUNttYQsCqbpd5KsglNgOx7_ZjbU,187955 +babel/locale-data/yue_Hans.dat,sha256=Y38xjfIlWQPNE4GWXsS08MPOa9F_fk4-h9s0bs1oa_8,187729 +babel/locale-data/yue_Hans_CN.dat,sha256=LJadpHvHHtY49YNddDLFCXFQh8RJwXFSMpSx-qskEuo,609 +babel/locale-data/yue_Hant.dat,sha256=_BWk9N_79PzY7EPWu-O_M8j1ISYhkN29HEbuo-i0AoI,1279 +babel/locale-data/yue_Hant_HK.dat,sha256=8iSo-1wkebNgS7h3iCFLd9s-nW8TuQ3-4UFUMUEmbMM,609 +babel/locale-data/zgh.dat,sha256=xFCWLD7dumIJBqgzCU_1wRCOpSOiPmbTAFbvlFdYWk8,30464 +babel/locale-data/zgh_MA.dat,sha256=sIGElmHSGowAduz_ykRA_-PotBTJaOqmBtRhXJ_swJc,590 +babel/locale-data/zh.dat,sha256=0qz6YQZYzXqfcjSRCLIU2R8UUvSyEh2Vpfz8YUzADsU,193691 +babel/locale-data/zh_Hans.dat,sha256=-JH1KTn0ibMkBj6gw2V2Q6ixLBL5x0n7B7AkCpU0TRI,1278 +babel/locale-data/zh_Hans_CN.dat,sha256=Ccjjc2VePpDa-XQ5MBBDghVwwjZ1H5sswYMOPDkonJM,608 +babel/locale-data/zh_Hans_HK.dat,sha256=5gkuu16wCLzaZLAZ6emlJl1fnOD_MewQ4shBTZXWg8A,3507 +babel/locale-data/zh_Hans_MO.dat,sha256=SenQEOeTcvvqR1aHo8RZ4yurasD1HznXq5sTVh4MiQs,3639 +babel/locale-data/zh_Hans_SG.dat,sha256=Wzt61cP2JJEU8PeMxjpmQ1u9ts_F-wcLRWWiDUYCf9s,3835 +babel/locale-data/zh_Hant.dat,sha256=pLo0B1ymfwCFfu_PwpYrjkbuIidi7cmeplWLd290x4U,199641 +babel/locale-data/zh_Hant_HK.dat,sha256=inVfOzruQGn1MRG0ygR55whUKoFEz3KeiG5TuiSvFWk,61576 +babel/locale-data/zh_Hant_MO.dat,sha256=gTS5IdcTsRoMwAnANseIbj2nyflLj0tBQn2-4AFAaf4,630 +babel/locale-data/zh_Hant_TW.dat,sha256=QYchBgi8JmeZgs7rQrUYn93CNNL9aavsOWYzAaTndwM,608 +babel/locale-data/zu.dat,sha256=p_Lozk977_nOCFVDDVqSeZ_sVeZ05ok8YoSjSpL_r6o,183445 +babel/locale-data/zu_ZA.dat,sha256=YO0tFWUiAdda0x3XEoL98oPNFGRLuk5OZ8DaR3lUX38,608 +babel/localedata.py,sha256=vdTBpZsR2ExG9jBJLe42Xg-NKbm7XU7Pg5z0lIXod1Y,8239 +babel/localtime/__init__.py,sha256=hHBBHcfWoTvnIOfahxHlxqNR7IL4A4yTuxxG7wDZWUk,1043 +babel/localtime/__pycache__/__init__.cpython-310.pyc,, +babel/localtime/__pycache__/_fallback.cpython-310.pyc,, +babel/localtime/__pycache__/_helpers.cpython-310.pyc,, +babel/localtime/__pycache__/_unix.cpython-310.pyc,, +babel/localtime/__pycache__/_win32.cpython-310.pyc,, +babel/localtime/_fallback.py,sha256=0ak6R7ATmobpIlVjzQOK667bCMUjaFGcBWfL-IJlFEo,1207 +babel/localtime/_helpers.py,sha256=cxCJUZ_rsEOyFEpaYiKUOcRDmcgM42o-5wEeeXO6t4w,1059 +babel/localtime/_unix.py,sha256=7ExS68O5KpjqgZ6Dhho2WhxlYQYe2etJiW8BW2mXMCc,3455 +babel/localtime/_win32.py,sha256=nSNnxSMOVAlvqvX50h9HSCzBfP7mj8njJ80JZ3pOIWk,3211 +babel/messages/__init__.py,sha256=7UsGpKWLQkcEBwmt1oLAW-7xNOXaGXJIBI1upvXH5yU,349 +babel/messages/__pycache__/__init__.cpython-310.pyc,, +babel/messages/__pycache__/catalog.cpython-310.pyc,, +babel/messages/__pycache__/checkers.cpython-310.pyc,, +babel/messages/__pycache__/extract.cpython-310.pyc,, +babel/messages/__pycache__/frontend.cpython-310.pyc,, +babel/messages/__pycache__/jslexer.cpython-310.pyc,, +babel/messages/__pycache__/mofile.cpython-310.pyc,, +babel/messages/__pycache__/plurals.cpython-310.pyc,, +babel/messages/__pycache__/pofile.cpython-310.pyc,, +babel/messages/catalog.py,sha256=yXWaE8QaypnLmrvyXFcLOwBEw05pK-g7wHz8UgTxHxs,36104 +babel/messages/checkers.py,sha256=utWd8-gYG3W1RgoDwNZCqwL273tjxLyZmxh8MxnFiMo,6436 +babel/messages/extract.py,sha256=ur7rcpTs64NZVpM9G16j71hN57CWZ0D0Kck7PZLziFQ,32360 +babel/messages/frontend.py,sha256=bn4jnUR4X1m-Seix1xPAcgaMjT5ztUeOjsipRd0TWRQ,41999 +babel/messages/jslexer.py,sha256=lLk5s_ez5srkHSPdxRmowUUyJif4NfUm7RmEdrPZrMk,7140 +babel/messages/mofile.py,sha256=KJhrAi0c97vrZa25BV9-VAnCmKdp-w2_DNymkPf7OaY,7345 +babel/messages/plurals.py,sha256=W9C7PG04tlAHzJ2jI-0MWKfH0wvyFvsezx-wUqSDhAg,7311 +babel/messages/pofile.py,sha256=h-9rFVj3FnRcipfgabvskvSLujT6A552FF8mXWsmCtw,22323 +babel/numbers.py,sha256=rgP8WxsvxElrwsFoPuXSRDD3w_0dV1WBDQS5R4_4zcM,49510 +babel/plural.py,sha256=8V9ipuNTQES9EBfbx8stRrOd1nHMstbsQmQ9BwHv7V8,23216 +babel/py.typed,sha256=DtCsIDq6KOv2NOEdQjTbeMWJKRh6ZEL2E-6Mf1RLeMA,59 +babel/support.py,sha256=LGdgHZ5J0X1HI1R2TXS2I3vKio3oLqieiEWETCU_yGc,26239 +babel/units.py,sha256=H4ut4eKiHtt01ZmB-Hej9WoVLJ3W9kqSfisWaUIA2DM,11880 +babel/util.py,sha256=1PIwDztbYvJC1e-KUVm5EiO8swmuaF-_wcae28b0QnI,7956 diff --git a/venv/lib/python3.10/site-packages/Babel-2.12.1.dist-info/WHEEL b/venv/lib/python3.10/site-packages/Babel-2.12.1.dist-info/WHEEL new file mode 100644 index 000000000..57e3d840d --- /dev/null +++ b/venv/lib/python3.10/site-packages/Babel-2.12.1.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.38.4) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/venv/lib/python3.10/site-packages/Babel-2.12.1.dist-info/entry_points.txt b/venv/lib/python3.10/site-packages/Babel-2.12.1.dist-info/entry_points.txt new file mode 100644 index 000000000..11ca6083b --- /dev/null +++ b/venv/lib/python3.10/site-packages/Babel-2.12.1.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.frontend:compile_catalog +extract_messages = babel.messages.frontend:extract_messages +init_catalog = babel.messages.frontend:init_catalog +update_catalog = babel.messages.frontend:update_catalog + +[distutils.setup_keywords] +message_extractors = babel.messages.frontend:check_message_extractors diff --git a/venv/lib/python3.10/site-packages/Babel-2.12.1.dist-info/top_level.txt b/venv/lib/python3.10/site-packages/Babel-2.12.1.dist-info/top_level.txt new file mode 100644 index 000000000..98f65931c --- /dev/null +++ b/venv/lib/python3.10/site-packages/Babel-2.12.1.dist-info/top_level.txt @@ -0,0 +1 @@ +babel diff --git a/venv/lib/python3.10/site-packages/GitPython-3.1.31.dist-info/AUTHORS b/venv/lib/python3.10/site-packages/GitPython-3.1.31.dist-info/AUTHORS new file mode 100644 index 000000000..8ccc09fc0 --- /dev/null +++ b/venv/lib/python3.10/site-packages/GitPython-3.1.31.dist-info/AUTHORS @@ -0,0 +1,54 @@ +GitPython was originally written by Michael Trier. +GitPython 0.2 was partially (re)written by Sebastian Thiel, based on 0.1.6 and git-dulwich. + +Contributors are: + +-Michael Trier +-Alan Briolat +-Florian Apolloner +-David Aguilar +-Jelmer Vernooij +-Steve Frécinaux +-Kai Lautaportti +-Paul Sowden +-Sebastian Thiel +-Jonathan Chu +-Vincent Driessen +-Phil Elson +-Bernard `Guyzmo` Pratz +-Timothy B. Hartman +-Konstantin Popov +-Peter Jones +-Anson Mansfield +-Ken Odegard +-Alexis Horgix Chotard +-Piotr Babij +-Mikuláš Poul +-Charles Bouchard-Légaré +-Yaroslav Halchenko +-Tim Swast +-William Luc Ritchie +-David Host +-A. Jesse Jiryu Davis +-Steven Whitman +-Stefan Stancu +-César Izurieta +-Arthur Milchior +-Anil Khatri +-JJ Graham +-Ben Thayer +-Dries Kennes +-Pratik Anurag +-Harmon +-Liam Beguin +-Ram Rachum +-Alba Mendez +-Robert Westman +-Hugo van Kemenade +-Hiroki Tokunaga +-Julien Mauroy +-Patrick Gerard +-Luke Twist +-Joseph Hale +-Santos Gallegos +Portions derived from other open source works and are clearly marked. diff --git a/venv/lib/python3.10/site-packages/GitPython-3.1.31.dist-info/INSTALLER b/venv/lib/python3.10/site-packages/GitPython-3.1.31.dist-info/INSTALLER new file mode 100644 index 000000000..a1b589e38 --- /dev/null +++ b/venv/lib/python3.10/site-packages/GitPython-3.1.31.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.10/site-packages/GitPython-3.1.31.dist-info/LICENSE b/venv/lib/python3.10/site-packages/GitPython-3.1.31.dist-info/LICENSE new file mode 100644 index 000000000..5a9a6f8d3 --- /dev/null +++ b/venv/lib/python3.10/site-packages/GitPython-3.1.31.dist-info/LICENSE @@ -0,0 +1,30 @@ +Copyright (C) 2008, 2009 Michael Trier 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: + +* 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 GitPython project 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 +OWNER 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.10/site-packages/GitPython-3.1.31.dist-info/METADATA b/venv/lib/python3.10/site-packages/GitPython-3.1.31.dist-info/METADATA new file mode 100644 index 000000000..6ec9708f7 --- /dev/null +++ b/venv/lib/python3.10/site-packages/GitPython-3.1.31.dist-info/METADATA @@ -0,0 +1,34 @@ +Metadata-Version: 2.1 +Name: GitPython +Version: 3.1.31 +Summary: GitPython is a Python library used to interact with Git repositories +Home-page: https://github.com/gitpython-developers/GitPython +Author: Sebastian Thiel, Michael Trier +Author-email: byronimo@gmail.com, mtrier@gmail.com +License: BSD +Platform: UNKNOWN +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: Operating System :: POSIX +Classifier: Operating System :: Microsoft :: Windows +Classifier: Operating System :: MacOS :: MacOS X +Classifier: Typing :: Typed +Classifier: Programming Language :: Python +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 :: 3.11 +Requires-Python: >=3.7 +Description-Content-Type: text/markdown +License-File: LICENSE +License-File: AUTHORS +Requires-Dist: gitdb (<5,>=4.0.1) +Requires-Dist: typing-extensions (>=3.7.4.3) ; python_version < "3.8" + +GitPython is a Python library used to interact with Git repositories + diff --git a/venv/lib/python3.10/site-packages/GitPython-3.1.31.dist-info/RECORD b/venv/lib/python3.10/site-packages/GitPython-3.1.31.dist-info/RECORD new file mode 100644 index 000000000..395079444 --- /dev/null +++ b/venv/lib/python3.10/site-packages/GitPython-3.1.31.dist-info/RECORD @@ -0,0 +1,82 @@ +GitPython-3.1.31.dist-info/AUTHORS,sha256=0F09KKrRmwH3zJ4gqo1tJMVlalC9bSunDNKlRvR6q2c,2158 +GitPython-3.1.31.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +GitPython-3.1.31.dist-info/LICENSE,sha256=_WV__CzvY9JceMq3gI1BTdA6KC5jiTSR_RHDL5i-Z_s,1521 +GitPython-3.1.31.dist-info/METADATA,sha256=zFy5SrG7Ur2UItx3seZXELCST9LBEX72wZa7Y7z7FSY,1340 +GitPython-3.1.31.dist-info/RECORD,, +GitPython-3.1.31.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92 +GitPython-3.1.31.dist-info/top_level.txt,sha256=0hzDuIp8obv624V3GmbqsagBWkk8ohtGU-Bc1PmTT0o,4 +git/__init__.py,sha256=O2tZaGpLYVQiK9lN3NucvyEoZcSFig13tAB6d2TTTL0,2342 +git/__pycache__/__init__.cpython-310.pyc,, +git/__pycache__/cmd.cpython-310.pyc,, +git/__pycache__/compat.cpython-310.pyc,, +git/__pycache__/config.cpython-310.pyc,, +git/__pycache__/db.cpython-310.pyc,, +git/__pycache__/diff.cpython-310.pyc,, +git/__pycache__/exc.cpython-310.pyc,, +git/__pycache__/remote.cpython-310.pyc,, +git/__pycache__/types.cpython-310.pyc,, +git/__pycache__/util.cpython-310.pyc,, +git/cmd.py,sha256=i4IyhmCTP-72NPO5aVeWhDT6_jLmA1C2qzhsS7G2UVw,53712 +git/compat.py,sha256=3wWLkD9QrZvLiV6NtNxJILwGrLE2nw_SoLqaTEPH364,2256 +git/config.py,sha256=PO6qicfkKwRFlKJr9AUuDrWV0rimlmb5S2wIVLlOd7w,34581 +git/db.py,sha256=dEs2Bn-iDuHyero9afw8mrXHrLE7_CDExv943iWU9WI,2244 +git/diff.py,sha256=DOWd26Dk7FqnKt79zpniv19muBzdYa949TcQPqVbZGg,23434 +git/exc.py,sha256=ys5ZYuvzvNN3TfcB5R_bUNRy3OEvURS5pJMdfy0Iws4,6446 +git/index/__init__.py,sha256=43ovvVNocVRNiQd4fLqvUMuGGmwhBQ9SsiQ46vkvk1E,89 +git/index/__pycache__/__init__.cpython-310.pyc,, +git/index/__pycache__/base.cpython-310.pyc,, +git/index/__pycache__/fun.cpython-310.pyc,, +git/index/__pycache__/typ.cpython-310.pyc,, +git/index/__pycache__/util.cpython-310.pyc,, +git/index/base.py,sha256=5GnqwmhLNF9f12hUq4rQyOvqzxPF1Fdc0QOETT5r010,57523 +git/index/fun.py,sha256=Y41IGlu8XqnradQXFjTGMISM45m8J256bTKs4xWR4qY,16406 +git/index/typ.py,sha256=QnyWeqzU7_xnyiwOki5W633Jp9g5COqEf6B4PeW3hK8,6252 +git/index/util.py,sha256=ISsWZjGiflooNr6XtElP4AhWUxQOakouvgeXC2PEepI,3475 +git/objects/__init__.py,sha256=NW8HBfdZvBYe9W6IjMWafSj_DVlV2REmmrpWKrHkGVw,692 +git/objects/__pycache__/__init__.cpython-310.pyc,, +git/objects/__pycache__/base.cpython-310.pyc,, +git/objects/__pycache__/blob.cpython-310.pyc,, +git/objects/__pycache__/commit.cpython-310.pyc,, +git/objects/__pycache__/fun.cpython-310.pyc,, +git/objects/__pycache__/tag.cpython-310.pyc,, +git/objects/__pycache__/tree.cpython-310.pyc,, +git/objects/__pycache__/util.cpython-310.pyc,, +git/objects/base.py,sha256=N2NTL9hLwgKqY-VQiar8Hvn4a41Y8o_Tmi_SR0mGAS8,7857 +git/objects/blob.py,sha256=FIbZTYniJ7nLsdrHuwhagFVs9tYoUIyXodRaHYLaQqs,986 +git/objects/commit.py,sha256=ji9ityweewpr12mHh9w3s3ubouYNNCRTBr-LBrjrPbs,27304 +git/objects/fun.py,sha256=SV3_G_jnEb_wEa5doF6AapX58StH3OGBxCAKeMyuA0I,8612 +git/objects/submodule/__init__.py,sha256=OsMeiex7cG6ev2f35IaJ5csH-eXchSoNKCt4HXUG5Ws,93 +git/objects/submodule/__pycache__/__init__.cpython-310.pyc,, +git/objects/submodule/__pycache__/base.cpython-310.pyc,, +git/objects/submodule/__pycache__/root.cpython-310.pyc,, +git/objects/submodule/__pycache__/util.cpython-310.pyc,, +git/objects/submodule/base.py,sha256=R4jTjBJyMjFOfDAYwsA6Q3Lt6qeFYERPE4PABACW6GE,61539 +git/objects/submodule/root.py,sha256=Ev_RnGzv4hi3UqEFMHuSR-uGR7kYpwOgwZFUG31X-Hc,19568 +git/objects/submodule/util.py,sha256=u2zQGFWBmryqET0XWf9BuiY1OOgWB8YCU3Wz0xdp4E4,3380 +git/objects/tag.py,sha256=ZXOLK_lV9E5G2aDl5t0hYDN2hhIhGF23HILHBnZgRX0,3840 +git/objects/tree.py,sha256=cSQbt3nn3cIrbVrBasB1wm2r-vzotYWhka1yDjOHf-k,14230 +git/objects/util.py,sha256=M8h53ueOV32nXE6XcnKhCHzXznT7pi8JpEEGgCNicXo,22275 +git/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +git/refs/__init__.py,sha256=PMF97jMUcivbCCEJnl2zTs-YtECNFp8rL8GHK8AitXU,203 +git/refs/__pycache__/__init__.cpython-310.pyc,, +git/refs/__pycache__/head.cpython-310.pyc,, +git/refs/__pycache__/log.cpython-310.pyc,, +git/refs/__pycache__/reference.cpython-310.pyc,, +git/refs/__pycache__/remote.cpython-310.pyc,, +git/refs/__pycache__/symbolic.cpython-310.pyc,, +git/refs/__pycache__/tag.cpython-310.pyc,, +git/refs/head.py,sha256=rZ4LbFd05Gs9sAuSU5VQRDmJZfrwMwWtBpLlmiUQ-Zg,9756 +git/refs/log.py,sha256=Z8X9_ZGZrVTWz9p_-fk1N3m47G-HTRPwozoZBDd70DI,11892 +git/refs/reference.py,sha256=DUx7QvYqTBeVxG53ntPfKCp3wuJyDBRIZcPCy1OD22s,5414 +git/refs/remote.py,sha256=E63Bh5ig1GYrk6FE46iNtS5P6ZgODyPXot8eJw-mxts,2556 +git/refs/symbolic.py,sha256=XwfeYr1Zp-fuHAoGuVAXKk4EYlsuUMVu99OjJWuWDTQ,29967 +git/refs/tag.py,sha256=FNoCZ3BdDl2i5kD3si2P9hoXU9rDAZ_YK0Rn84TmKT8,4419 +git/remote.py,sha256=H88bonpIjnfozWScpQIFIccE7Soq2hfHO9ldnRCmGUY,45069 +git/repo/__init__.py,sha256=XMpdeowJRtTEd80jAcrKSQfMu2JZGMfPlpuIYHG2ZCk,80 +git/repo/__pycache__/__init__.cpython-310.pyc,, +git/repo/__pycache__/base.cpython-310.pyc,, +git/repo/__pycache__/fun.cpython-310.pyc,, +git/repo/base.py,sha256=uD4EL2AWUMSCHCqIk7voXoZ2iChaf5VJ1t1Abr7Zk10,54937 +git/repo/fun.py,sha256=VTRODXAb_x8bazkSd8g-Pkk8M2iLVK4kPoKQY9HXjZc,12962 +git/types.py,sha256=bA4El-NC7YNwQ9jNtkbWgT0QmmAfVs4PVSwBOE_D1Bo,3020 +git/util.py,sha256=j5cjyeFibLs4Ed_ErkePf6sx1VWb95OQ4GlJUWgq6PU,39874 diff --git a/venv/lib/python3.10/site-packages/GitPython-3.1.31.dist-info/WHEEL b/venv/lib/python3.10/site-packages/GitPython-3.1.31.dist-info/WHEEL new file mode 100644 index 000000000..5bad85fdc --- /dev/null +++ b/venv/lib/python3.10/site-packages/GitPython-3.1.31.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.37.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/venv/lib/python3.10/site-packages/GitPython-3.1.31.dist-info/top_level.txt b/venv/lib/python3.10/site-packages/GitPython-3.1.31.dist-info/top_level.txt new file mode 100644 index 000000000..5664e303b --- /dev/null +++ b/venv/lib/python3.10/site-packages/GitPython-3.1.31.dist-info/top_level.txt @@ -0,0 +1 @@ +git diff --git a/venv/lib/python3.10/site-packages/Jinja2-3.1.2.dist-info/INSTALLER b/venv/lib/python3.10/site-packages/Jinja2-3.1.2.dist-info/INSTALLER new file mode 100644 index 000000000..a1b589e38 --- /dev/null +++ b/venv/lib/python3.10/site-packages/Jinja2-3.1.2.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.10/site-packages/Jinja2-3.1.2.dist-info/LICENSE.rst b/venv/lib/python3.10/site-packages/Jinja2-3.1.2.dist-info/LICENSE.rst new file mode 100644 index 000000000..c37cae49e --- /dev/null +++ b/venv/lib/python3.10/site-packages/Jinja2-3.1.2.dist-info/LICENSE.rst @@ -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.10/site-packages/Jinja2-3.1.2.dist-info/METADATA b/venv/lib/python3.10/site-packages/Jinja2-3.1.2.dist-info/METADATA new file mode 100644 index 000000000..f54bb5ca1 --- /dev/null +++ b/venv/lib/python3.10/site-packages/Jinja2-3.1.2.dist-info/METADATA @@ -0,0 +1,113 @@ +Metadata-Version: 2.1 +Name: Jinja2 +Version: 3.1.2 +Summary: A very fast and expressive template engine. +Home-page: https://palletsprojects.com/p/jinja/ +Author: Armin Ronacher +Author-email: armin.ronacher@active-4.com +Maintainer: Pallets +Maintainer-email: contact@palletsprojects.com +License: BSD-3-Clause +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Documentation, https://jinja.palletsprojects.com/ +Project-URL: Changes, https://jinja.palletsprojects.com/changes/ +Project-URL: Source Code, https://github.com/pallets/jinja/ +Project-URL: Issue Tracker, https://github.com/pallets/jinja/issues/ +Project-URL: Twitter, https://twitter.com/PalletsTeam +Project-URL: Chat, https://discord.gg/pallets +Platform: UNKNOWN +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 +Requires-Python: >=3.7 +Description-Content-Type: text/x-rst +License-File: LICENSE.rst +Requires-Dist: MarkupSafe (>=2.0) +Provides-Extra: i18n +Requires-Dist: Babel (>=2.7) ; 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. + + +Installing +---------- + +Install and update using `pip`_: + +.. code-block:: text + + $ pip install -U Jinja2 + +.. _pip: https://pip.pypa.io/en/stable/getting-started/ + + +In A Nutshell +------------- + +.. code-block:: 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 + + +Links +----- + +- Documentation: https://jinja.palletsprojects.com/ +- Changes: https://jinja.palletsprojects.com/changes/ +- PyPI Releases: https://pypi.org/project/Jinja2/ +- Source Code: https://github.com/pallets/jinja/ +- Issue Tracker: https://github.com/pallets/jinja/issues/ +- Website: https://palletsprojects.com/p/jinja/ +- Twitter: https://twitter.com/PalletsTeam +- Chat: https://discord.gg/pallets + + diff --git a/venv/lib/python3.10/site-packages/Jinja2-3.1.2.dist-info/RECORD b/venv/lib/python3.10/site-packages/Jinja2-3.1.2.dist-info/RECORD new file mode 100644 index 000000000..b821b4fae --- /dev/null +++ b/venv/lib/python3.10/site-packages/Jinja2-3.1.2.dist-info/RECORD @@ -0,0 +1,58 @@ +Jinja2-3.1.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +Jinja2-3.1.2.dist-info/LICENSE.rst,sha256=O0nc7kEF6ze6wQ-vG-JgQI_oXSUrjp3y4JefweCUQ3s,1475 +Jinja2-3.1.2.dist-info/METADATA,sha256=PZ6v2SIidMNixR7MRUX9f7ZWsPwtXanknqiZUmRbh4U,3539 +Jinja2-3.1.2.dist-info/RECORD,, +Jinja2-3.1.2.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92 +Jinja2-3.1.2.dist-info/entry_points.txt,sha256=zRd62fbqIyfUpsRtU7EVIFyiu1tPwfgO7EvPErnxgTE,59 +Jinja2-3.1.2.dist-info/top_level.txt,sha256=PkeVWtLb3-CqjWi1fO29OCbj55EhX_chhKrCdrVe_zs,7 +jinja2/__init__.py,sha256=8vGduD8ytwgD6GDSqpYc2m3aU-T7PKOAddvVXgGr_Fs,1927 +jinja2/__pycache__/__init__.cpython-310.pyc,, +jinja2/__pycache__/_identifier.cpython-310.pyc,, +jinja2/__pycache__/async_utils.cpython-310.pyc,, +jinja2/__pycache__/bccache.cpython-310.pyc,, +jinja2/__pycache__/compiler.cpython-310.pyc,, +jinja2/__pycache__/constants.cpython-310.pyc,, +jinja2/__pycache__/debug.cpython-310.pyc,, +jinja2/__pycache__/defaults.cpython-310.pyc,, +jinja2/__pycache__/environment.cpython-310.pyc,, +jinja2/__pycache__/exceptions.cpython-310.pyc,, +jinja2/__pycache__/ext.cpython-310.pyc,, +jinja2/__pycache__/filters.cpython-310.pyc,, +jinja2/__pycache__/idtracking.cpython-310.pyc,, +jinja2/__pycache__/lexer.cpython-310.pyc,, +jinja2/__pycache__/loaders.cpython-310.pyc,, +jinja2/__pycache__/meta.cpython-310.pyc,, +jinja2/__pycache__/nativetypes.cpython-310.pyc,, +jinja2/__pycache__/nodes.cpython-310.pyc,, +jinja2/__pycache__/optimizer.cpython-310.pyc,, +jinja2/__pycache__/parser.cpython-310.pyc,, +jinja2/__pycache__/runtime.cpython-310.pyc,, +jinja2/__pycache__/sandbox.cpython-310.pyc,, +jinja2/__pycache__/tests.cpython-310.pyc,, +jinja2/__pycache__/utils.cpython-310.pyc,, +jinja2/__pycache__/visitor.cpython-310.pyc,, +jinja2/_identifier.py,sha256=_zYctNKzRqlk_murTNlzrju1FFJL7Va_Ijqqd7ii2lU,1958 +jinja2/async_utils.py,sha256=dHlbTeaxFPtAOQEYOGYh_PHcDT0rsDaUJAFDl_0XtTg,2472 +jinja2/bccache.py,sha256=mhz5xtLxCcHRAa56azOhphIAe19u1we0ojifNMClDio,14061 +jinja2/compiler.py,sha256=Gs-N8ThJ7OWK4-reKoO8Wh1ZXz95MVphBKNVf75qBr8,72172 +jinja2/constants.py,sha256=GMoFydBF_kdpaRKPoM5cl5MviquVRLVyZtfp5-16jg0,1433 +jinja2/debug.py,sha256=iWJ432RadxJNnaMOPrjIDInz50UEgni3_HKuFXi2vuQ,6299 +jinja2/defaults.py,sha256=boBcSw78h-lp20YbaXSJsqkAI2uN_mD_TtCydpeq5wU,1267 +jinja2/environment.py,sha256=6uHIcc7ZblqOMdx_uYNKqRnnwAF0_nzbyeMP9FFtuh4,61349 +jinja2/exceptions.py,sha256=ioHeHrWwCWNaXX1inHmHVblvc4haO7AXsjCp3GfWvx0,5071 +jinja2/ext.py,sha256=ivr3P7LKbddiXDVez20EflcO3q2aHQwz9P_PgWGHVqE,31502 +jinja2/filters.py,sha256=9js1V-h2RlyW90IhLiBGLM2U-k6SCy2F4BUUMgB3K9Q,53509 +jinja2/idtracking.py,sha256=GfNmadir4oDALVxzn3DL9YInhJDr69ebXeA2ygfuCGA,10704 +jinja2/lexer.py,sha256=DW2nX9zk-6MWp65YR2bqqj0xqCvLtD-u9NWT8AnFRxQ,29726 +jinja2/loaders.py,sha256=BfptfvTVpClUd-leMkHczdyPNYFzp_n7PKOJ98iyHOg,23207 +jinja2/meta.py,sha256=GNPEvifmSaU3CMxlbheBOZjeZ277HThOPUTf1RkppKQ,4396 +jinja2/nativetypes.py,sha256=DXgORDPRmVWgy034H0xL8eF7qYoK3DrMxs-935d0Fzk,4226 +jinja2/nodes.py,sha256=i34GPRAZexXMT6bwuf5SEyvdmS-bRCy9KMjwN5O6pjk,34550 +jinja2/optimizer.py,sha256=tHkMwXxfZkbfA1KmLcqmBMSaz7RLIvvItrJcPoXTyD8,1650 +jinja2/parser.py,sha256=nHd-DFHbiygvfaPtm9rcQXJChZG7DPsWfiEsqfwKerY,39595 +jinja2/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +jinja2/runtime.py,sha256=5CmD5BjbEJxSiDNTFBeKCaq8qU4aYD2v6q2EluyExms,33476 +jinja2/sandbox.py,sha256=Y0xZeXQnH6EX5VjaV2YixESxoepnRbW_3UeQosaBU3M,14584 +jinja2/tests.py,sha256=Am5Z6Lmfr2XaH_npIfJJ8MdXtWsbLjMULZJulTAj30E,5905 +jinja2/utils.py,sha256=u9jXESxGn8ATZNVolwmkjUVu4SA-tLgV0W7PcSfPfdQ,23965 +jinja2/visitor.py,sha256=MH14C6yq24G_KVtWzjwaI7Wg14PCJIYlWW1kpkxYak0,3568 diff --git a/venv/lib/python3.10/site-packages/Jinja2-3.1.2.dist-info/WHEEL b/venv/lib/python3.10/site-packages/Jinja2-3.1.2.dist-info/WHEEL new file mode 100644 index 000000000..becc9a66e --- /dev/null +++ b/venv/lib/python3.10/site-packages/Jinja2-3.1.2.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.10/site-packages/Jinja2-3.1.2.dist-info/entry_points.txt b/venv/lib/python3.10/site-packages/Jinja2-3.1.2.dist-info/entry_points.txt new file mode 100644 index 000000000..7b9666c8e --- /dev/null +++ b/venv/lib/python3.10/site-packages/Jinja2-3.1.2.dist-info/entry_points.txt @@ -0,0 +1,2 @@ +[babel.extractors] +jinja2 = jinja2.ext:babel_extract[i18n] diff --git a/venv/lib/python3.10/site-packages/Jinja2-3.1.2.dist-info/top_level.txt b/venv/lib/python3.10/site-packages/Jinja2-3.1.2.dist-info/top_level.txt new file mode 100644 index 000000000..7f7afbf3b --- /dev/null +++ b/venv/lib/python3.10/site-packages/Jinja2-3.1.2.dist-info/top_level.txt @@ -0,0 +1 @@ +jinja2 diff --git a/venv/lib/python3.10/site-packages/Markdown-3.3.7.dist-info/INSTALLER b/venv/lib/python3.10/site-packages/Markdown-3.3.7.dist-info/INSTALLER new file mode 100644 index 000000000..a1b589e38 --- /dev/null +++ b/venv/lib/python3.10/site-packages/Markdown-3.3.7.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.10/site-packages/Markdown-3.3.7.dist-info/LICENSE.md b/venv/lib/python3.10/site-packages/Markdown-3.3.7.dist-info/LICENSE.md new file mode 100644 index 000000000..2652d97ad --- /dev/null +++ b/venv/lib/python3.10/site-packages/Markdown-3.3.7.dist-info/LICENSE.md @@ -0,0 +1,29 @@ +Copyright 2007, 2008 The Python Markdown Project (v. 1.7 and later) +Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) +Copyright 2004 Manfred Stienstra (the original version) + +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 Python Markdown Project 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 PYTHON MARKDOWN PROJECT ''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 ANY CONTRIBUTORS TO THE PYTHON MARKDOWN PROJECT +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.10/site-packages/Markdown-3.3.7.dist-info/METADATA b/venv/lib/python3.10/site-packages/Markdown-3.3.7.dist-info/METADATA new file mode 100644 index 000000000..3763fae43 --- /dev/null +++ b/venv/lib/python3.10/site-packages/Markdown-3.3.7.dist-info/METADATA @@ -0,0 +1,109 @@ +Metadata-Version: 2.1 +Name: Markdown +Version: 3.3.7 +Summary: Python implementation of Markdown. +Home-page: https://Python-Markdown.github.io/ +Author: Manfred Stienstra, Yuri takhteyev and Waylan limberg +Author-email: python.markdown@gmail.com +Maintainer: Waylan Limberg +Maintainer-email: python.markdown@gmail.com +License: BSD License +Project-URL: Documentation, https://Python-Markdown.github.io/ +Project-URL: GitHub Project, https://github.com/Python-Markdown/markdown +Project-URL: Issue Tracker, https://github.com/Python-Markdown/markdown/issues +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +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.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 :: Only +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Communications :: Email :: Filters +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content :: CGI Tools/Libraries +Classifier: Topic :: Internet :: WWW/HTTP :: Site Management +Classifier: Topic :: Software Development :: Documentation +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: Text Processing :: Filters +Classifier: Topic :: Text Processing :: Markup :: HTML +Classifier: Topic :: Text Processing :: Markup :: Markdown +Requires-Python: >=3.6 +Description-Content-Type: text/markdown +License-File: LICENSE.md +Requires-Dist: importlib-metadata (>=4.4) ; python_version < "3.10" +Provides-Extra: testing +Requires-Dist: coverage ; extra == 'testing' +Requires-Dist: pyyaml ; extra == 'testing' + +[Python-Markdown][] +=================== + +[![Build Status][build-button]][build] +[![Coverage Status][codecov-button]][codecov] +[![Latest Version][mdversion-button]][md-pypi] +[![Python Versions][pyversion-button]][md-pypi] +[![BSD License][bsdlicense-button]][bsdlicense] +[![Code of Conduct][codeofconduct-button]][Code of Conduct] + +[build-button]: https://github.com/Python-Markdown/markdown/workflows/CI/badge.svg?event=push +[build]: https://github.com/Python-Markdown/markdown/actions?query=workflow%3ACI+event%3Apush +[codecov-button]: https://codecov.io/gh/Python-Markdown/markdown/branch/master/graph/badge.svg +[codecov]: https://codecov.io/gh/Python-Markdown/markdown +[mdversion-button]: https://img.shields.io/pypi/v/Markdown.svg +[md-pypi]: https://pypi.org/project/Markdown/ +[pyversion-button]: https://img.shields.io/pypi/pyversions/Markdown.svg +[bsdlicense-button]: https://img.shields.io/badge/license-BSD-yellow.svg +[bsdlicense]: https://opensource.org/licenses/BSD-3-Clause +[codeofconduct-button]: https://img.shields.io/badge/code%20of%20conduct-contributor%20covenant-green.svg?style=flat-square +[Code of Conduct]: https://github.com/Python-Markdown/markdown/blob/master/CODE_OF_CONDUCT.md + +This is a Python implementation of John Gruber's [Markdown][]. +It is almost completely compliant with the reference implementation, +though there are a few known issues. See [Features][] for information +on what exactly is supported and what is not. Additional features are +supported by the [Available Extensions][]. + +[Python-Markdown]: https://Python-Markdown.github.io/ +[Markdown]: https://daringfireball.net/projects/markdown/ +[Features]: https://Python-Markdown.github.io#Features +[Available Extensions]: https://Python-Markdown.github.io/extensions + +Documentation +------------- + +```bash +pip install markdown +``` +```python +import markdown +html = markdown.markdown(your_text_string) +``` + +For more advanced [installation] and [usage] documentation, see the `docs/` directory +of the distribution or the project website at . + +[installation]: https://python-markdown.github.io/install/ +[usage]: https://python-markdown.github.io/reference/ + +See the change log at . + +Support +------- + +You may report bugs, ask for help, and discuss various other issues on the [bug tracker][]. + +[bug tracker]: https://github.com/Python-Markdown/markdown/issues + +Code of Conduct +--------------- + +Everyone interacting in the Python-Markdown project's codebases, issue trackers, +and mailing lists is expected to follow the [Code of Conduct]. + + diff --git a/venv/lib/python3.10/site-packages/Markdown-3.3.7.dist-info/RECORD b/venv/lib/python3.10/site-packages/Markdown-3.3.7.dist-info/RECORD new file mode 100644 index 000000000..a8e673d2b --- /dev/null +++ b/venv/lib/python3.10/site-packages/Markdown-3.3.7.dist-info/RECORD @@ -0,0 +1,76 @@ +../../../bin/markdown_py,sha256=kpzoIUjCZQoP92s5-HRLdHLWxo0ONsEqLANd8N6zplo,289 +Markdown-3.3.7.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +Markdown-3.3.7.dist-info/LICENSE.md,sha256=bxGTy2NHGOZcOlN9biXr1hSCDsDvaTz8EiSBEmONZNo,1645 +Markdown-3.3.7.dist-info/METADATA,sha256=yDPc4gkbiMBomDr_gIAPsQ8zVfkYoe9mRkon4KZ889A,4629 +Markdown-3.3.7.dist-info/RECORD,, +Markdown-3.3.7.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92 +Markdown-3.3.7.dist-info/entry_points.txt,sha256=lMEyiiA_ZZyfPCBlDviBl-SiU0cfoeuEKpwxw361sKQ,1102 +Markdown-3.3.7.dist-info/top_level.txt,sha256=IAxs8x618RXoH1uCqeLLxXsDefJvE_mIibr_M4sOlyk,9 +markdown/__init__.py,sha256=002-LuHviYzROW2rg_gBGai81nMouUNO9UFj5nSsTSk,2065 +markdown/__main__.py,sha256=JX1057VoovH3NA5uH5nQdQE8b0kXoeT79ZxCzFoL_kg,5803 +markdown/__meta__.py,sha256=AjQfLZ5mSCOzSQk-HNK3jedyEPUyaZMTKwyXjRxCHso,1630 +markdown/__pycache__/__init__.cpython-310.pyc,, +markdown/__pycache__/__main__.cpython-310.pyc,, +markdown/__pycache__/__meta__.cpython-310.pyc,, +markdown/__pycache__/blockparser.cpython-310.pyc,, +markdown/__pycache__/blockprocessors.cpython-310.pyc,, +markdown/__pycache__/core.cpython-310.pyc,, +markdown/__pycache__/htmlparser.cpython-310.pyc,, +markdown/__pycache__/inlinepatterns.cpython-310.pyc,, +markdown/__pycache__/pep562.cpython-310.pyc,, +markdown/__pycache__/postprocessors.cpython-310.pyc,, +markdown/__pycache__/preprocessors.cpython-310.pyc,, +markdown/__pycache__/serializers.cpython-310.pyc,, +markdown/__pycache__/test_tools.cpython-310.pyc,, +markdown/__pycache__/treeprocessors.cpython-310.pyc,, +markdown/__pycache__/util.cpython-310.pyc,, +markdown/blockparser.py,sha256=JpBhOokOoBUGCXolftOc5m1hPcR2y9s9hVd9WSuhHzo,4285 +markdown/blockprocessors.py,sha256=sUHIdAsGGAEN443TusbiNOWYt38DsHV-RqJdGjumIFc,24893 +markdown/core.py,sha256=ZHtqvLdVHOKWIuX_UzdL3rIcxMwji5TC5ZCkV19iM4U,15401 +markdown/extensions/__init__.py,sha256=kw5SehW1-IRGZk-ZR4aLQp2F0Qqj-Qql35qIBA8efnU,3663 +markdown/extensions/__pycache__/__init__.cpython-310.pyc,, +markdown/extensions/__pycache__/abbr.cpython-310.pyc,, +markdown/extensions/__pycache__/admonition.cpython-310.pyc,, +markdown/extensions/__pycache__/attr_list.cpython-310.pyc,, +markdown/extensions/__pycache__/codehilite.cpython-310.pyc,, +markdown/extensions/__pycache__/def_list.cpython-310.pyc,, +markdown/extensions/__pycache__/extra.cpython-310.pyc,, +markdown/extensions/__pycache__/fenced_code.cpython-310.pyc,, +markdown/extensions/__pycache__/footnotes.cpython-310.pyc,, +markdown/extensions/__pycache__/legacy_attrs.cpython-310.pyc,, +markdown/extensions/__pycache__/legacy_em.cpython-310.pyc,, +markdown/extensions/__pycache__/md_in_html.cpython-310.pyc,, +markdown/extensions/__pycache__/meta.cpython-310.pyc,, +markdown/extensions/__pycache__/nl2br.cpython-310.pyc,, +markdown/extensions/__pycache__/sane_lists.cpython-310.pyc,, +markdown/extensions/__pycache__/smarty.cpython-310.pyc,, +markdown/extensions/__pycache__/tables.cpython-310.pyc,, +markdown/extensions/__pycache__/toc.cpython-310.pyc,, +markdown/extensions/__pycache__/wikilinks.cpython-310.pyc,, +markdown/extensions/abbr.py,sha256=5TNU5ml6-H1n-fztEkgUphSTvp5yKCXaiPZMrVuRFvo,3186 +markdown/extensions/admonition.py,sha256=INIecvdzQ7RLmgP8M-N6AZJ5uMd6dBfh9Uj6YibgNLk,5847 +markdown/extensions/attr_list.py,sha256=nhKFY_u6BVyKW2oMUeC4wEjqFNGpDSnNXqaohuF6M7I,5988 +markdown/extensions/codehilite.py,sha256=C4Jiuc-EwQQaedIGo_2sGyoZR6YgTR0FocusKWZc6Vg,11710 +markdown/extensions/def_list.py,sha256=HKStriCfwosWRjHgiph6hHIHwGjasEaE6UYW-_hstVo,3635 +markdown/extensions/extra.py,sha256=ruwYAcbIaFxAmcT4pLoaRdw8Ok6sFTYWza7OAstcvtI,1831 +markdown/extensions/fenced_code.py,sha256=Hd2RDaRWcCd4aI9fedoI6EElPmIVrD2BlvXdhRV64ik,7209 +markdown/extensions/footnotes.py,sha256=Ux13UAjNiptuyvCnDIII89YWTZ0DTrEmkrwcyen7ZgM,15485 +markdown/extensions/legacy_attrs.py,sha256=qx4d8c_mxt0JZ7wP9Sfskvi3cZN-OtDGTFCi4gapZ74,2547 +markdown/extensions/legacy_em.py,sha256=8mtzOGYu_FXKO7DrBVr_5v5ZH6ru1yv1TiobYBEFV5Q,1582 +markdown/extensions/md_in_html.py,sha256=F4CUIa2DjDPLEIuJCbmbw9jL1mbFloPhraedynZL9Ig,15829 +markdown/extensions/meta.py,sha256=EUfkzM7l7UpH__Or9K3pl8ldVddwndlCZWA3d712RAE,2331 +markdown/extensions/nl2br.py,sha256=wAqTNOuf2L1NzlEvEqoID70n9y-aiYaGLkuyQk3CD0w,783 +markdown/extensions/sane_lists.py,sha256=ZQmCf-247KBexVG0fc62nDvokGkV6W1uavYbieNKSG4,1505 +markdown/extensions/smarty.py,sha256=0padzkVCNACainKw-Xj1S5UfT0125VCTfNejmrCZItA,10238 +markdown/extensions/tables.py,sha256=u8gmond8fQqJB4nVcljvDG5EvEueMPn_UaPEp5o9hF0,7685 +markdown/extensions/toc.py,sha256=uOGElJ4K4-BAYGHdjwUT1Xv9iQ-PAgwI493NpyoRC9g,14136 +markdown/extensions/wikilinks.py,sha256=GkgT9BY7b1-qW--dIwFAhC9V20RoeF13b7CFdw_V21Q,2812 +markdown/htmlparser.py,sha256=Eios9Ui8L5IvdmlN8aTXioiTgOrrnUrxH2kPpLhgU0U,13033 +markdown/inlinepatterns.py,sha256=nVzm5T01WvCMSyLeopmVwacvYYBvQw0Ac-Svj_TnudI,29775 +markdown/pep562.py,sha256=5UkqT7sb-cQufgbOl_jF-RYUVVHS7VThzlMzR9vrd3I,8917 +markdown/postprocessors.py,sha256=NeJyWBqPeDuBBJLTGs5Bfm5oTkUBXk9HWBeQy2_OldI,4262 +markdown/preprocessors.py,sha256=-s8QGHGlX7JAIJTfCivuc-CVwTLWs0IyEU94YUT2IvQ,2742 +markdown/serializers.py,sha256=_wQl-iJrPSUEQ4Q1owWYqN9qceVh6TOlAOH_i44BKAQ,6540 +markdown/test_tools.py,sha256=Iht9NMNtDgtrMCNdjkMN9EWXjgsJjaFzJQVvzxo1Da0,8363 +markdown/treeprocessors.py,sha256=MIdj6cv1YiIafGFk8wy-hhV3ZQfgvuIdMErwA7286Ns,15434 +markdown/util.py,sha256=Xo8dhPcIwzYZ7RzzIGXoeC3Nq41aaP16B7JO076e28A,16063 diff --git a/venv/lib/python3.10/site-packages/Markdown-3.3.7.dist-info/WHEEL b/venv/lib/python3.10/site-packages/Markdown-3.3.7.dist-info/WHEEL new file mode 100644 index 000000000..becc9a66e --- /dev/null +++ b/venv/lib/python3.10/site-packages/Markdown-3.3.7.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.10/site-packages/Markdown-3.3.7.dist-info/entry_points.txt b/venv/lib/python3.10/site-packages/Markdown-3.3.7.dist-info/entry_points.txt new file mode 100644 index 000000000..be3bd8ff2 --- /dev/null +++ b/venv/lib/python3.10/site-packages/Markdown-3.3.7.dist-info/entry_points.txt @@ -0,0 +1,22 @@ +[console_scripts] +markdown_py = markdown.__main__:run + +[markdown.extensions] +abbr = markdown.extensions.abbr:AbbrExtension +admonition = markdown.extensions.admonition:AdmonitionExtension +attr_list = markdown.extensions.attr_list:AttrListExtension +codehilite = markdown.extensions.codehilite:CodeHiliteExtension +def_list = markdown.extensions.def_list:DefListExtension +extra = markdown.extensions.extra:ExtraExtension +fenced_code = markdown.extensions.fenced_code:FencedCodeExtension +footnotes = markdown.extensions.footnotes:FootnoteExtension +legacy_attrs = markdown.extensions.legacy_attrs:LegacyAttrExtension +legacy_em = markdown.extensions.legacy_em:LegacyEmExtension +md_in_html = markdown.extensions.md_in_html:MarkdownInHtmlExtension +meta = markdown.extensions.meta:MetaExtension +nl2br = markdown.extensions.nl2br:Nl2BrExtension +sane_lists = markdown.extensions.sane_lists:SaneListExtension +smarty = markdown.extensions.smarty:SmartyExtension +tables = markdown.extensions.tables:TableExtension +toc = markdown.extensions.toc:TocExtension +wikilinks = markdown.extensions.wikilinks:WikiLinkExtension diff --git a/venv/lib/python3.10/site-packages/Markdown-3.3.7.dist-info/top_level.txt b/venv/lib/python3.10/site-packages/Markdown-3.3.7.dist-info/top_level.txt new file mode 100644 index 000000000..0918c9768 --- /dev/null +++ b/venv/lib/python3.10/site-packages/Markdown-3.3.7.dist-info/top_level.txt @@ -0,0 +1 @@ +markdown diff --git a/venv/lib/python3.10/site-packages/MarkupSafe-2.1.2.dist-info/INSTALLER b/venv/lib/python3.10/site-packages/MarkupSafe-2.1.2.dist-info/INSTALLER new file mode 100644 index 000000000..a1b589e38 --- /dev/null +++ b/venv/lib/python3.10/site-packages/MarkupSafe-2.1.2.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.10/site-packages/MarkupSafe-2.1.2.dist-info/LICENSE.rst b/venv/lib/python3.10/site-packages/MarkupSafe-2.1.2.dist-info/LICENSE.rst new file mode 100644 index 000000000..9d227a0cc --- /dev/null +++ b/venv/lib/python3.10/site-packages/MarkupSafe-2.1.2.dist-info/LICENSE.rst @@ -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.10/site-packages/MarkupSafe-2.1.2.dist-info/METADATA b/venv/lib/python3.10/site-packages/MarkupSafe-2.1.2.dist-info/METADATA new file mode 100644 index 000000000..4a3499979 --- /dev/null +++ b/venv/lib/python3.10/site-packages/MarkupSafe-2.1.2.dist-info/METADATA @@ -0,0 +1,98 @@ +Metadata-Version: 2.1 +Name: MarkupSafe +Version: 2.1.2 +Summary: Safely add untrusted strings to HTML/XML markup. +Home-page: https://palletsprojects.com/p/markupsafe/ +Author: Armin Ronacher +Author-email: armin.ronacher@active-4.com +Maintainer: Pallets +Maintainer-email: contact@palletsprojects.com +License: BSD-3-Clause +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 Code, https://github.com/pallets/markupsafe/ +Project-URL: Issue Tracker, https://github.com/pallets/markupsafe/issues/ +Project-URL: Twitter, https://twitter.com/PalletsTeam +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 +Requires-Python: >=3.7 +Description-Content-Type: text/x-rst +License-File: LICENSE.rst + +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. + + +Installing +---------- + +Install and update using `pip`_: + +.. code-block:: text + + pip install -U MarkupSafe + +.. _pip: https://pip.pypa.io/en/stable/getting-started/ + + +Examples +-------- + +.. code-block:: 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 + + +Links +----- + +- Documentation: https://markupsafe.palletsprojects.com/ +- Changes: https://markupsafe.palletsprojects.com/changes/ +- PyPI Releases: https://pypi.org/project/MarkupSafe/ +- Source Code: https://github.com/pallets/markupsafe/ +- Issue Tracker: https://github.com/pallets/markupsafe/issues/ +- Website: https://palletsprojects.com/p/markupsafe/ +- Twitter: https://twitter.com/PalletsTeam +- Chat: https://discord.gg/pallets diff --git a/venv/lib/python3.10/site-packages/MarkupSafe-2.1.2.dist-info/RECORD b/venv/lib/python3.10/site-packages/MarkupSafe-2.1.2.dist-info/RECORD new file mode 100644 index 000000000..5302cde05 --- /dev/null +++ b/venv/lib/python3.10/site-packages/MarkupSafe-2.1.2.dist-info/RECORD @@ -0,0 +1,14 @@ +MarkupSafe-2.1.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +MarkupSafe-2.1.2.dist-info/LICENSE.rst,sha256=SJqOEQhQntmKN7uYPhHg9-HTHwvY-Zp5yESOf_N9B-o,1475 +MarkupSafe-2.1.2.dist-info/METADATA,sha256=jPw4iOiZg6adxZ5bdvjZapeNmPMINMGG2q2v2rI4SqA,3222 +MarkupSafe-2.1.2.dist-info/RECORD,, +MarkupSafe-2.1.2.dist-info/WHEEL,sha256=DyGia2S6qP_FD4mwU9COmmXmXHC-xHTstdLzZxR3BQs,115 +MarkupSafe-2.1.2.dist-info/top_level.txt,sha256=qy0Plje5IJuvsCBjejJyhDCjEAdcDLK_2agVcex8Z6U,11 +markupsafe/__init__.py,sha256=LtjnhQ6AHmAgHl37cev2oQBXjr4xOF-QhdXgsCAL3-0,9306 +markupsafe/__pycache__/__init__.cpython-310.pyc,, +markupsafe/__pycache__/_native.cpython-310.pyc,, +markupsafe/_native.py,sha256=GR86Qvo_GcgKmKreA1WmYN9ud17OFwkww8E-fiW-57s,1713 +markupsafe/_speedups.c,sha256=X2XvQVtIdcK4Usz70BvkzoOfjTCmQlDkkjYSn-swE0g,7083 +markupsafe/_speedups.cpython-310-darwin.so,sha256=4pn_LUAvobTH37Z_-1HDoxk696gq1h_y6xiBGDfUcZs,117484 +markupsafe/_speedups.pyi,sha256=vfMCsOgbAXRNLUXkyuyonG8uEWKYU4PDqNuMaDELAYw,229 +markupsafe/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/venv/lib/python3.10/site-packages/MarkupSafe-2.1.2.dist-info/WHEEL b/venv/lib/python3.10/site-packages/MarkupSafe-2.1.2.dist-info/WHEEL new file mode 100644 index 000000000..d5634d737 --- /dev/null +++ b/venv/lib/python3.10/site-packages/MarkupSafe-2.1.2.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.38.4) +Root-Is-Purelib: false +Tag: cp310-cp310-macosx_10_9_universal2 + diff --git a/venv/lib/python3.10/site-packages/MarkupSafe-2.1.2.dist-info/top_level.txt b/venv/lib/python3.10/site-packages/MarkupSafe-2.1.2.dist-info/top_level.txt new file mode 100644 index 000000000..75bf72925 --- /dev/null +++ b/venv/lib/python3.10/site-packages/MarkupSafe-2.1.2.dist-info/top_level.txt @@ -0,0 +1 @@ +markupsafe diff --git a/venv/lib/python3.10/site-packages/PyYAML-6.0.dist-info/INSTALLER b/venv/lib/python3.10/site-packages/PyYAML-6.0.dist-info/INSTALLER new file mode 100644 index 000000000..a1b589e38 --- /dev/null +++ b/venv/lib/python3.10/site-packages/PyYAML-6.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.10/site-packages/PyYAML-6.0.dist-info/LICENSE b/venv/lib/python3.10/site-packages/PyYAML-6.0.dist-info/LICENSE new file mode 100644 index 000000000..2f1b8e15e --- /dev/null +++ b/venv/lib/python3.10/site-packages/PyYAML-6.0.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.10/site-packages/PyYAML-6.0.dist-info/METADATA b/venv/lib/python3.10/site-packages/PyYAML-6.0.dist-info/METADATA new file mode 100644 index 000000000..9a910762b --- /dev/null +++ b/venv/lib/python3.10/site-packages/PyYAML-6.0.dist-info/METADATA @@ -0,0 +1,46 @@ +Metadata-Version: 2.1 +Name: PyYAML +Version: 6.0 +Summary: YAML parser and emitter for Python +Home-page: https://pyyaml.org/ +Author: Kirill Simonov +Author-email: xi@resolvent.net +License: MIT +Download-URL: https://pypi.org/project/PyYAML/ +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.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 :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: Text Processing :: Markup +Requires-Python: >=3.6 +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.10/site-packages/PyYAML-6.0.dist-info/RECORD b/venv/lib/python3.10/site-packages/PyYAML-6.0.dist-info/RECORD new file mode 100644 index 000000000..91d14beb3 --- /dev/null +++ b/venv/lib/python3.10/site-packages/PyYAML-6.0.dist-info/RECORD @@ -0,0 +1,43 @@ +PyYAML-6.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +PyYAML-6.0.dist-info/LICENSE,sha256=jTko-dxEkP1jVwfLiOsmvXZBAqcoKVQwfT5RZ6V36KQ,1101 +PyYAML-6.0.dist-info/METADATA,sha256=QmHx9kGp_0yezQCXYaft4eEFeJ6W4oyFfYwHDLP1kdg,2006 +PyYAML-6.0.dist-info/RECORD,, +PyYAML-6.0.dist-info/WHEEL,sha256=eKsqltxboaz7GOtugUvyM_YdId8c0DWeUFKvJbaZVUI,110 +PyYAML-6.0.dist-info/top_level.txt,sha256=rpj0IVMTisAjh_1vG3Ccf9v5jpCQwAz6cD1IVU5ZdhQ,11 +_yaml/__init__.py,sha256=04Ae_5osxahpJHa3XBZUAf4wi6XX32gR8D6X6p64GEA,1402 +_yaml/__pycache__/__init__.cpython-310.pyc,, +yaml/__init__.py,sha256=NDS7S8XgA72-hY6LRmGzUWTPvzGzjWVrWk-OGA-77AA,12309 +yaml/__pycache__/__init__.cpython-310.pyc,, +yaml/__pycache__/composer.cpython-310.pyc,, +yaml/__pycache__/constructor.cpython-310.pyc,, +yaml/__pycache__/cyaml.cpython-310.pyc,, +yaml/__pycache__/dumper.cpython-310.pyc,, +yaml/__pycache__/emitter.cpython-310.pyc,, +yaml/__pycache__/error.cpython-310.pyc,, +yaml/__pycache__/events.cpython-310.pyc,, +yaml/__pycache__/loader.cpython-310.pyc,, +yaml/__pycache__/nodes.cpython-310.pyc,, +yaml/__pycache__/parser.cpython-310.pyc,, +yaml/__pycache__/reader.cpython-310.pyc,, +yaml/__pycache__/representer.cpython-310.pyc,, +yaml/__pycache__/resolver.cpython-310.pyc,, +yaml/__pycache__/scanner.cpython-310.pyc,, +yaml/__pycache__/serializer.cpython-310.pyc,, +yaml/__pycache__/tokens.cpython-310.pyc,, +yaml/_yaml.cpython-310-darwin.so,sha256=H-S4lsvRaF96wwRy1Crxfeqje9Z6LI6NnDFpE1-XcCE,378968 +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.10/site-packages/PyYAML-6.0.dist-info/WHEEL b/venv/lib/python3.10/site-packages/PyYAML-6.0.dist-info/WHEEL new file mode 100644 index 000000000..5257e3202 --- /dev/null +++ b/venv/lib/python3.10/site-packages/PyYAML-6.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.37.0) +Root-Is-Purelib: false +Tag: cp310-cp310-macosx_11_0_arm64 + diff --git a/venv/lib/python3.10/site-packages/PyYAML-6.0.dist-info/top_level.txt b/venv/lib/python3.10/site-packages/PyYAML-6.0.dist-info/top_level.txt new file mode 100644 index 000000000..e6475e911 --- /dev/null +++ b/venv/lib/python3.10/site-packages/PyYAML-6.0.dist-info/top_level.txt @@ -0,0 +1,2 @@ +_yaml +yaml diff --git a/venv/lib/python3.10/site-packages/Pygments-2.15.1.dist-info/AUTHORS b/venv/lib/python3.10/site-packages/Pygments-2.15.1.dist-info/AUTHORS new file mode 100644 index 000000000..20a8ca6a3 --- /dev/null +++ b/venv/lib/python3.10/site-packages/Pygments-2.15.1.dist-info/AUTHORS @@ -0,0 +1,270 @@ +Pygments is written and maintained by Georg Brandl . + +Major developers are Tim Hatch and Armin Ronacher +. + +Other contributors, listed alphabetically, are: + +* Sam Aaron -- Ioke lexer +* Jean Abou Samra -- LilyPond lexer +* João Abecasis -- JSLT lexer +* Ali Afshar -- image formatter +* Thomas Aglassinger -- Easytrieve, JCL, Rexx, Transact-SQL and VBScript + lexers +* Maxence Ahlouche -- PostgreSQL Explain lexer +* Muthiah Annamalai -- Ezhil lexer +* Kumar Appaiah -- Debian control lexer +* Andreas Amann -- AppleScript lexer +* Timothy Armstrong -- Dart lexer fixes +* Jeffrey Arnold -- R/S, Rd, BUGS, Jags, and Stan lexers +* Eiríkr Åsheim -- Uxntal lexer +* Jeremy Ashkenas -- CoffeeScript lexer +* José Joaquín Atria -- Praat lexer +* Stefan Matthias Aust -- Smalltalk lexer +* Lucas Bajolet -- Nit lexer +* Ben Bangert -- Mako lexers +* Max Battcher -- Darcs patch lexer +* Thomas Baruchel -- APL lexer +* Tim Baumann -- (Literate) Agda lexer +* Paul Baumgart, 280 North, Inc. -- Objective-J lexer +* Michael Bayer -- Myghty lexers +* Thomas Beale -- Archetype lexers +* John Benediktsson -- Factor lexer +* Trevor Bergeron -- mIRC formatter +* Vincent Bernat -- LessCSS lexer +* Christopher Bertels -- Fancy lexer +* Sébastien Bigaret -- QVT Operational lexer +* Jarrett Billingsley -- MiniD lexer +* Adam Blinkinsop -- Haskell, Redcode lexers +* Stéphane Blondon -- Procfile, SGF and Sieve lexers +* Frits van Bommel -- assembler lexers +* Pierre Bourdon -- bugfixes +* Martijn Braam -- Kernel log lexer, BARE lexer +* Matthias Bussonnier -- ANSI style handling for terminal-256 formatter +* chebee7i -- Python traceback lexer improvements +* Hiram Chirino -- Scaml and Jade lexers +* Mauricio Caceres -- SAS and Stata lexers. +* Ian Cooper -- VGL lexer +* David Corbett -- Inform, Jasmin, JSGF, Snowball, and TADS 3 lexers +* Leaf Corcoran -- MoonScript lexer +* Christopher Creutzig -- MuPAD lexer +* Daniël W. Crompton -- Pike lexer +* Pete Curry -- bugfixes +* Bryan Davis -- EBNF lexer +* Bruno Deferrari -- Shen lexer +* Walter Dörwald -- UL4 lexer +* Luke Drummond -- Meson lexer +* Giedrius Dubinskas -- HTML formatter improvements +* Owen Durni -- Haxe lexer +* Alexander Dutton, Oxford University Computing Services -- SPARQL lexer +* James Edwards -- Terraform lexer +* Nick Efford -- Python 3 lexer +* Sven Efftinge -- Xtend lexer +* Artem Egorkine -- terminal256 formatter +* Matthew Fernandez -- CAmkES lexer +* Paweł Fertyk -- GDScript lexer, HTML formatter improvements +* Michael Ficarra -- CPSA lexer +* James H. Fisher -- PostScript lexer +* William S. Fulton -- SWIG lexer +* Carlos Galdino -- Elixir and Elixir Console lexers +* Michael Galloy -- IDL lexer +* Naveen Garg -- Autohotkey lexer +* Simon Garnotel -- FreeFem++ lexer +* Laurent Gautier -- R/S lexer +* Alex Gaynor -- PyPy log lexer +* Richard Gerkin -- Igor Pro lexer +* Alain Gilbert -- TypeScript lexer +* Alex Gilding -- BlitzBasic lexer +* GitHub, Inc -- DASM16, Augeas, TOML, and Slash lexers +* Bertrand Goetzmann -- Groovy lexer +* Krzysiek Goj -- Scala lexer +* Rostyslav Golda -- FloScript lexer +* Andrey Golovizin -- BibTeX lexers +* Matt Good -- Genshi, Cheetah lexers +* Michał Górny -- vim modeline support +* Alex Gosse -- TrafficScript lexer +* Patrick Gotthardt -- PHP namespaces support +* Hubert Gruniaux -- C and C++ lexer improvements +* Olivier Guibe -- Asymptote lexer +* Phil Hagelberg -- Fennel lexer +* Florian Hahn -- Boogie lexer +* Martin Harriman -- SNOBOL lexer +* Matthew Harrison -- SVG formatter +* Steven Hazel -- Tcl lexer +* Dan Michael Heggø -- Turtle lexer +* Aslak Hellesøy -- Gherkin lexer +* Greg Hendershott -- Racket lexer +* Justin Hendrick -- ParaSail lexer +* Jordi Gutiérrez Hermoso -- Octave lexer +* David Hess, Fish Software, Inc. -- Objective-J lexer +* Ken Hilton -- Typographic Number Theory and Arrow lexers +* Varun Hiremath -- Debian control lexer +* Rob Hoelz -- Perl 6 lexer +* Doug Hogan -- Mscgen lexer +* Ben Hollis -- Mason lexer +* Max Horn -- GAP lexer +* Fred Hornsey -- OMG IDL Lexer +* Alastair Houghton -- Lexer inheritance facility +* Tim Howard -- BlitzMax lexer +* Dustin Howett -- Logos lexer +* Ivan Inozemtsev -- Fantom lexer +* Hiroaki Itoh -- Shell console rewrite, Lexers for PowerShell session, + MSDOS session, BC, WDiff +* Brian R. Jackson -- Tea lexer +* Christian Jann -- ShellSession lexer +* Dennis Kaarsemaker -- sources.list lexer +* Dmitri Kabak -- Inferno Limbo lexer +* Igor Kalnitsky -- vhdl lexer +* Colin Kennedy - USD lexer +* Alexander Kit -- MaskJS lexer +* Pekka Klärck -- Robot Framework lexer +* Gerwin Klein -- Isabelle lexer +* Eric Knibbe -- Lasso lexer +* Stepan Koltsov -- Clay lexer +* Oliver Kopp - Friendly grayscale style +* Adam Koprowski -- Opa lexer +* Benjamin Kowarsch -- Modula-2 lexer +* Domen Kožar -- Nix lexer +* Oleh Krekel -- Emacs Lisp lexer +* Alexander Kriegisch -- Kconfig and AspectJ lexers +* Marek Kubica -- Scheme lexer +* Jochen Kupperschmidt -- Markdown processor +* Gerd Kurzbach -- Modelica lexer +* Jon Larimer, Google Inc. -- Smali lexer +* Olov Lassus -- Dart lexer +* Matt Layman -- TAP lexer +* Kristian Lyngstøl -- Varnish lexers +* Sylvestre Ledru -- Scilab lexer +* Chee Sing Lee -- Flatline lexer +* Mark Lee -- Vala lexer +* Pete Lomax -- Phix lexer +* Valentin Lorentz -- C++ lexer improvements +* Ben Mabey -- Gherkin lexer +* Angus MacArthur -- QML lexer +* Louis Mandel -- X10 lexer +* Louis Marchand -- Eiffel lexer +* Simone Margaritelli -- Hybris lexer +* Tim Martin - World of Warcraft TOC lexer +* Kirk McDonald -- D lexer +* Gordon McGregor -- SystemVerilog lexer +* Stephen McKamey -- Duel/JBST lexer +* Brian McKenna -- F# lexer +* Charles McLaughlin -- Puppet lexer +* Kurt McKee -- Tera Term macro lexer, PostgreSQL updates, MySQL overhaul, JSON lexer +* Joe Eli McIlvain -- Savi lexer +* Lukas Meuser -- BBCode formatter, Lua lexer +* Cat Miller -- Pig lexer +* Paul Miller -- LiveScript lexer +* Hong Minhee -- HTTP lexer +* Michael Mior -- Awk lexer +* Bruce Mitchener -- Dylan lexer rewrite +* Reuben Morais -- SourcePawn lexer +* Jon Morton -- Rust lexer +* Paulo Moura -- Logtalk lexer +* Mher Movsisyan -- DTD lexer +* Dejan Muhamedagic -- Crmsh lexer +* Adrien Nayrat -- PostgreSQL Explain lexer +* Ana Nelson -- Ragel, ANTLR, R console lexers +* David Neto, Google LLC -- WebGPU Shading Language lexer +* Kurt Neufeld -- Markdown lexer +* Nam T. Nguyen -- Monokai style +* Jesper Noehr -- HTML formatter "anchorlinenos" +* Mike Nolta -- Julia lexer +* Avery Nortonsmith -- Pointless lexer +* Jonas Obrist -- BBCode lexer +* Edward O'Callaghan -- Cryptol lexer +* David Oliva -- Rebol lexer +* Pat Pannuto -- nesC lexer +* Jon Parise -- Protocol buffers and Thrift lexers +* Benjamin Peterson -- Test suite refactoring +* Ronny Pfannschmidt -- BBCode lexer +* Dominik Picheta -- Nimrod lexer +* Andrew Pinkham -- RTF Formatter Refactoring +* Clément Prévost -- UrbiScript lexer +* Tanner Prynn -- cmdline -x option and loading lexers from files +* Oleh Prypin -- Crystal lexer (based on Ruby lexer) +* Nick Psaris -- K and Q lexers +* Xidorn Quan -- Web IDL lexer +* Elias Rabel -- Fortran fixed form lexer +* raichoo -- Idris lexer +* Daniel Ramirez -- GDScript lexer +* Kashif Rasul -- CUDA lexer +* Nathan Reed -- HLSL lexer +* Justin Reidy -- MXML lexer +* Norman Richards -- JSON lexer +* Corey Richardson -- Rust lexer updates +* Fabrizio Riguzzi -- cplint leder +* Lubomir Rintel -- GoodData MAQL and CL lexers +* Andre Roberge -- Tango style +* Georg Rollinger -- HSAIL lexer +* Michiel Roos -- TypoScript lexer +* Konrad Rudolph -- LaTeX formatter enhancements +* Mario Ruggier -- Evoque lexers +* Miikka Salminen -- Lovelace style, Hexdump lexer, lexer enhancements +* Stou Sandalski -- NumPy, FORTRAN, tcsh and XSLT lexers +* Matteo Sasso -- Common Lisp lexer +* Joe Schafer -- Ada lexer +* Max Schillinger -- TiddlyWiki5 lexer +* Andrew Schmidt -- X++ lexer +* Ken Schutte -- Matlab lexers +* René Schwaiger -- Rainbow Dash style +* Sebastian Schweizer -- Whiley lexer +* Tassilo Schweyer -- Io, MOOCode lexers +* Pablo Seminario -- PromQL lexer +* Ted Shaw -- AutoIt lexer +* Joerg Sieker -- ABAP lexer +* Robert Simmons -- Standard ML lexer +* Kirill Simonov -- YAML lexer +* Corbin Simpson -- Monte lexer +* Ville Skyttä -- ASCII armored lexer +* Alexander Smishlajev -- Visual FoxPro lexer +* Steve Spigarelli -- XQuery lexer +* Jerome St-Louis -- eC lexer +* Camil Staps -- Clean and NuSMV lexers; Solarized style +* James Strachan -- Kotlin lexer +* Tom Stuart -- Treetop lexer +* Colin Sullivan -- SuperCollider lexer +* Ben Swift -- Extempore lexer +* tatt61880 -- Kuin lexer +* Edoardo Tenani -- Arduino lexer +* Tiberius Teng -- default style overhaul +* Jeremy Thurgood -- Erlang, Squid config lexers +* Brian Tiffin -- OpenCOBOL lexer +* Bob Tolbert -- Hy lexer +* Doug Torrance -- Macaulay2 lexer +* Matthias Trute -- Forth lexer +* Tuoa Spi T4 -- Bdd lexer +* Erick Tryzelaar -- Felix lexer +* Alexander Udalov -- Kotlin lexer improvements +* Thomas Van Doren -- Chapel lexer +* Daniele Varrazzo -- PostgreSQL lexers +* Abe Voelker -- OpenEdge ABL lexer +* Pepijn de Vos -- HTML formatter CTags support +* Matthias Vallentin -- Bro lexer +* Benoît Vinot -- AMPL lexer +* Linh Vu Hong -- RSL lexer +* Immanuel Washington -- Smithy lexer +* Nathan Weizenbaum -- Haml and Sass lexers +* Nathan Whetsell -- Csound lexers +* Dietmar Winkler -- Modelica lexer +* Nils Winter -- Smalltalk lexer +* Davy Wybiral -- Clojure lexer +* Whitney Young -- ObjectiveC lexer +* Diego Zamboni -- CFengine3 lexer +* Enrique Zamudio -- Ceylon lexer +* Alex Zimin -- Nemerle lexer +* Rob Zimmerman -- Kal lexer +* Vincent Zurczak -- Roboconf lexer +* Hubert Gruniaux -- C and C++ lexer improvements +* Thomas Symalla -- AMDGPU Lexer +* 15b3 -- Image Formatter improvements +* Fabian Neumann -- CDDL lexer +* Thomas Duboucher -- CDDL lexer +* Philipp Imhof -- Pango Markup formatter +* Thomas Voss -- Sed lexer +* Martin Fischer -- WCAG contrast testing +* Marc Auberer -- Spice lexer +* Amr Hesham -- Carbon lexer +* diskdance -- Wikitext lexer + +Many thanks for all contributions! diff --git a/venv/lib/python3.10/site-packages/Pygments-2.15.1.dist-info/INSTALLER b/venv/lib/python3.10/site-packages/Pygments-2.15.1.dist-info/INSTALLER new file mode 100644 index 000000000..a1b589e38 --- /dev/null +++ b/venv/lib/python3.10/site-packages/Pygments-2.15.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.10/site-packages/Pygments-2.15.1.dist-info/LICENSE b/venv/lib/python3.10/site-packages/Pygments-2.15.1.dist-info/LICENSE new file mode 100644 index 000000000..446a1a805 --- /dev/null +++ b/venv/lib/python3.10/site-packages/Pygments-2.15.1.dist-info/LICENSE @@ -0,0 +1,25 @@ +Copyright (c) 2006-2022 by the respective authors (see AUTHORS file). +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. + +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 +OWNER 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.10/site-packages/Pygments-2.15.1.dist-info/METADATA b/venv/lib/python3.10/site-packages/Pygments-2.15.1.dist-info/METADATA new file mode 100644 index 000000000..bc31410e6 --- /dev/null +++ b/venv/lib/python3.10/site-packages/Pygments-2.15.1.dist-info/METADATA @@ -0,0 +1,56 @@ +Metadata-Version: 2.1 +Name: Pygments +Version: 2.15.1 +Summary: Pygments is a syntax highlighting package written in Python. +Author-email: Georg Brandl +Maintainer: Matthäus G. Chajdas +Maintainer-email: Georg Brandl , Jean Abou Samra +License: BSD-2-Clause +Project-URL: Homepage, https://pygments.org +Project-URL: Documentation, https://pygments.org/docs +Project-URL: Source, https://github.com/pygments/pygments +Project-URL: Bug Tracker, https://github.com/pygments/pygments/issues +Project-URL: Changelog, https://github.com/pygments/pygments/blob/master/CHANGES +Keywords: syntax highlighting +Classifier: Development Status :: 6 - Mature +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: End Users/Desktop +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.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 :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Text Processing :: Filters +Classifier: Topic :: Utilities +Requires-Python: >=3.7 +Description-Content-Type: text/x-rst +License-File: LICENSE +License-File: AUTHORS +Provides-Extra: plugins +Requires-Dist: importlib-metadata ; (python_version < "3.8") and extra == 'plugins' + +Pygments +~~~~~~~~ + +Pygments is a syntax highlighting package written in Python. + +It is a generic syntax highlighter suitable for use in code hosting, forums, +wikis or other applications that need to prettify source code. Highlights +are: + +* a wide range of over 500 languages and other text formats is supported +* special attention is paid to details, increasing quality by a fair amount +* support for new languages and formats are added easily +* a number of output formats, presently HTML, LaTeX, RTF, SVG, all image + formats that PIL supports and ANSI sequences +* it is usable as a command-line tool and as a library + +Copyright 2006-2023 by the Pygments team, see ``AUTHORS``. +Licensed under the BSD, see ``LICENSE`` for details. diff --git a/venv/lib/python3.10/site-packages/Pygments-2.15.1.dist-info/RECORD b/venv/lib/python3.10/site-packages/Pygments-2.15.1.dist-info/RECORD new file mode 100644 index 000000000..f6463f3de --- /dev/null +++ b/venv/lib/python3.10/site-packages/Pygments-2.15.1.dist-info/RECORD @@ -0,0 +1,613 @@ +../../../bin/pygmentize,sha256=n3kIjrT-nC6dIHlovE4tqQtQPJBnFqyWNTDexQGU6xI,290 +Pygments-2.15.1.dist-info/AUTHORS,sha256=TdJddtUy7LNm2wA_GLgRetvaopiRjjIIA5WOHzsI1WE,9974 +Pygments-2.15.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +Pygments-2.15.1.dist-info/LICENSE,sha256=qdZvHVJt8C4p3Oc0NtNOVuhjL0bCdbvf_HBWnogvnxc,1331 +Pygments-2.15.1.dist-info/METADATA,sha256=0vGsTBqE3q3Pi2-2JjCrSimfsh4ejRVGbddETm8IXv4,2451 +Pygments-2.15.1.dist-info/RECORD,, +Pygments-2.15.1.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92 +Pygments-2.15.1.dist-info/entry_points.txt,sha256=uUXw-XhMKBEX4pWcCtpuTTnPhL3h7OEE2jWi51VQsa8,53 +Pygments-2.15.1.dist-info/top_level.txt,sha256=RjKKqrVIStoebLHdbs0yZ2Lk4rS7cxGguXsLCYvZ2Ak,9 +pygments/__init__.py,sha256=KXCoLMvtF-oa7UOyFz4JxCrZe0f_7BGHMs7xzz2LFP0,2959 +pygments/__main__.py,sha256=xVzk7wG94kskOfLT6ORXWRPRZZ0IAavOeRCtGXn2yEM,348 +pygments/__pycache__/__init__.cpython-310.pyc,, +pygments/__pycache__/__main__.cpython-310.pyc,, +pygments/__pycache__/cmdline.cpython-310.pyc,, +pygments/__pycache__/console.cpython-310.pyc,, +pygments/__pycache__/filter.cpython-310.pyc,, +pygments/__pycache__/formatter.cpython-310.pyc,, +pygments/__pycache__/lexer.cpython-310.pyc,, +pygments/__pycache__/modeline.cpython-310.pyc,, +pygments/__pycache__/plugin.cpython-310.pyc,, +pygments/__pycache__/regexopt.cpython-310.pyc,, +pygments/__pycache__/scanner.cpython-310.pyc,, +pygments/__pycache__/sphinxext.cpython-310.pyc,, +pygments/__pycache__/style.cpython-310.pyc,, +pygments/__pycache__/token.cpython-310.pyc,, +pygments/__pycache__/unistring.cpython-310.pyc,, +pygments/__pycache__/util.cpython-310.pyc,, +pygments/cmdline.py,sha256=5BDrKb_cOcvzp0Ps8rr4rFIO8kYb15JDlEiRKpoTwWE,23530 +pygments/console.py,sha256=2wZ5W-U6TudJD1_NLUwjclMpbomFM91lNv11_60sfGY,1697 +pygments/filter.py,sha256=j5aLM9a9wSx6eH1oy473oSkJ02hGWNptBlVo4s1g_30,1938 +pygments/filters/__init__.py,sha256=q8r8GfK3dO021TSHJdpwNwh6RHKbAL7ceCzohavWaN0,40338 +pygments/filters/__pycache__/__init__.cpython-310.pyc,, +pygments/formatter.py,sha256=Yz01yT1NSQXTF8GvsLxJSmez7ErTVOsxm3M6GVPuH-w,4154 +pygments/formatters/__init__.py,sha256=iRJiIk5375wDS08XLtJgTYEHzLd1_uvDGG0C85zQsfs,5388 +pygments/formatters/__pycache__/__init__.cpython-310.pyc,, +pygments/formatters/__pycache__/_mapping.cpython-310.pyc,, +pygments/formatters/__pycache__/bbcode.cpython-310.pyc,, +pygments/formatters/__pycache__/groff.cpython-310.pyc,, +pygments/formatters/__pycache__/html.cpython-310.pyc,, +pygments/formatters/__pycache__/img.cpython-310.pyc,, +pygments/formatters/__pycache__/irc.cpython-310.pyc,, +pygments/formatters/__pycache__/latex.cpython-310.pyc,, +pygments/formatters/__pycache__/other.cpython-310.pyc,, +pygments/formatters/__pycache__/pangomarkup.cpython-310.pyc,, +pygments/formatters/__pycache__/rtf.cpython-310.pyc,, +pygments/formatters/__pycache__/svg.cpython-310.pyc,, +pygments/formatters/__pycache__/terminal.cpython-310.pyc,, +pygments/formatters/__pycache__/terminal256.cpython-310.pyc,, +pygments/formatters/_mapping.py,sha256=1Cw37FuQlNacnxRKmtlPX4nyLoX9_ttko5ZwscNUZZ4,4176 +pygments/formatters/bbcode.py,sha256=7zcNcnoNj-dCyJlrHnmtq0V2o8ZGD14N5anFV4FQ5PE,3290 +pygments/formatters/groff.py,sha256=va2xlSCN-e_i0c_Dz0dg3HV9Ajt9G45PXaCi7CEsLdg,5070 +pygments/formatters/html.py,sha256=C0BdFXcODAHHHHVRiE5G6G3dUG_BHk938w90w-og1ZI,35574 +pygments/formatters/img.py,sha256=kzcmeDavdndLUV9TDy-LLdr3OwlhUfep1dAT7H-nQoY,21914 +pygments/formatters/irc.py,sha256=Jkze75ztayIkhNijfnFIsqOUR-Yz5fwEQM_oeWdOnRU,4945 +pygments/formatters/latex.py,sha256=48gMb8ziYgyQVJ4Ofx8x3Ka2l0RBomgYgn5zjeQK_vM,19303 +pygments/formatters/other.py,sha256=RzJhlXeYHyLxiC5vESy41WUK0FOqJPxvMiR6Von0z5Q,5025 +pygments/formatters/pangomarkup.py,sha256=0i4H7NLsxcxzFCJU5ONDJmhlLvsSLCnaFn-FXA5SMQk,2200 +pygments/formatters/rtf.py,sha256=8xghaGMBBXq2XNj-ZWNzYAeX_GqIDMqUBiJLDCulzNI,4990 +pygments/formatters/svg.py,sha256=VxjE-iqarQXJEFRissuLbsooXUCVXWgObQ7c4ub-5zg,7299 +pygments/formatters/terminal.py,sha256=P_dr7GLyRTIKg8LEceCOr5B5eGzGQBELA3JQOMvZ8VA,4626 +pygments/formatters/terminal256.py,sha256=_xYtNUUTAie5Bh7S98nrt7XIlLUURzDykosP7tT7044,11717 +pygments/lexer.py,sha256=MXnx0PyK98ubxsr6GQQ7D3kJey3CQJeqcJlr6vpmpjA,34541 +pygments/lexers/__init__.py,sha256=zIVAQGUmmzrjrNGeOEn0j178RNUqIb6bN8dnqIihfSA,12082 +pygments/lexers/__pycache__/__init__.cpython-310.pyc,, +pygments/lexers/__pycache__/_ada_builtins.cpython-310.pyc,, +pygments/lexers/__pycache__/_asy_builtins.cpython-310.pyc,, +pygments/lexers/__pycache__/_cl_builtins.cpython-310.pyc,, +pygments/lexers/__pycache__/_cocoa_builtins.cpython-310.pyc,, +pygments/lexers/__pycache__/_csound_builtins.cpython-310.pyc,, +pygments/lexers/__pycache__/_css_builtins.cpython-310.pyc,, +pygments/lexers/__pycache__/_julia_builtins.cpython-310.pyc,, +pygments/lexers/__pycache__/_lasso_builtins.cpython-310.pyc,, +pygments/lexers/__pycache__/_lilypond_builtins.cpython-310.pyc,, +pygments/lexers/__pycache__/_lua_builtins.cpython-310.pyc,, +pygments/lexers/__pycache__/_mapping.cpython-310.pyc,, +pygments/lexers/__pycache__/_mql_builtins.cpython-310.pyc,, +pygments/lexers/__pycache__/_mysql_builtins.cpython-310.pyc,, +pygments/lexers/__pycache__/_openedge_builtins.cpython-310.pyc,, +pygments/lexers/__pycache__/_php_builtins.cpython-310.pyc,, +pygments/lexers/__pycache__/_postgres_builtins.cpython-310.pyc,, +pygments/lexers/__pycache__/_qlik_builtins.cpython-310.pyc,, +pygments/lexers/__pycache__/_scheme_builtins.cpython-310.pyc,, +pygments/lexers/__pycache__/_scilab_builtins.cpython-310.pyc,, +pygments/lexers/__pycache__/_sourcemod_builtins.cpython-310.pyc,, +pygments/lexers/__pycache__/_stan_builtins.cpython-310.pyc,, +pygments/lexers/__pycache__/_stata_builtins.cpython-310.pyc,, +pygments/lexers/__pycache__/_tsql_builtins.cpython-310.pyc,, +pygments/lexers/__pycache__/_usd_builtins.cpython-310.pyc,, +pygments/lexers/__pycache__/_vbscript_builtins.cpython-310.pyc,, +pygments/lexers/__pycache__/_vim_builtins.cpython-310.pyc,, +pygments/lexers/__pycache__/actionscript.cpython-310.pyc,, +pygments/lexers/__pycache__/ada.cpython-310.pyc,, +pygments/lexers/__pycache__/agile.cpython-310.pyc,, +pygments/lexers/__pycache__/algebra.cpython-310.pyc,, +pygments/lexers/__pycache__/ambient.cpython-310.pyc,, +pygments/lexers/__pycache__/amdgpu.cpython-310.pyc,, +pygments/lexers/__pycache__/ampl.cpython-310.pyc,, +pygments/lexers/__pycache__/apdlexer.cpython-310.pyc,, +pygments/lexers/__pycache__/apl.cpython-310.pyc,, +pygments/lexers/__pycache__/archetype.cpython-310.pyc,, +pygments/lexers/__pycache__/arrow.cpython-310.pyc,, +pygments/lexers/__pycache__/arturo.cpython-310.pyc,, +pygments/lexers/__pycache__/asc.cpython-310.pyc,, +pygments/lexers/__pycache__/asm.cpython-310.pyc,, +pygments/lexers/__pycache__/automation.cpython-310.pyc,, +pygments/lexers/__pycache__/bare.cpython-310.pyc,, +pygments/lexers/__pycache__/basic.cpython-310.pyc,, +pygments/lexers/__pycache__/bdd.cpython-310.pyc,, +pygments/lexers/__pycache__/berry.cpython-310.pyc,, +pygments/lexers/__pycache__/bibtex.cpython-310.pyc,, +pygments/lexers/__pycache__/boa.cpython-310.pyc,, +pygments/lexers/__pycache__/business.cpython-310.pyc,, +pygments/lexers/__pycache__/c_cpp.cpython-310.pyc,, +pygments/lexers/__pycache__/c_like.cpython-310.pyc,, +pygments/lexers/__pycache__/capnproto.cpython-310.pyc,, +pygments/lexers/__pycache__/carbon.cpython-310.pyc,, +pygments/lexers/__pycache__/cddl.cpython-310.pyc,, +pygments/lexers/__pycache__/chapel.cpython-310.pyc,, +pygments/lexers/__pycache__/clean.cpython-310.pyc,, +pygments/lexers/__pycache__/comal.cpython-310.pyc,, +pygments/lexers/__pycache__/compiled.cpython-310.pyc,, +pygments/lexers/__pycache__/configs.cpython-310.pyc,, +pygments/lexers/__pycache__/console.cpython-310.pyc,, +pygments/lexers/__pycache__/cplint.cpython-310.pyc,, +pygments/lexers/__pycache__/crystal.cpython-310.pyc,, +pygments/lexers/__pycache__/csound.cpython-310.pyc,, +pygments/lexers/__pycache__/css.cpython-310.pyc,, +pygments/lexers/__pycache__/d.cpython-310.pyc,, +pygments/lexers/__pycache__/dalvik.cpython-310.pyc,, +pygments/lexers/__pycache__/data.cpython-310.pyc,, +pygments/lexers/__pycache__/dax.cpython-310.pyc,, +pygments/lexers/__pycache__/devicetree.cpython-310.pyc,, +pygments/lexers/__pycache__/diff.cpython-310.pyc,, +pygments/lexers/__pycache__/dotnet.cpython-310.pyc,, +pygments/lexers/__pycache__/dsls.cpython-310.pyc,, +pygments/lexers/__pycache__/dylan.cpython-310.pyc,, +pygments/lexers/__pycache__/ecl.cpython-310.pyc,, +pygments/lexers/__pycache__/eiffel.cpython-310.pyc,, +pygments/lexers/__pycache__/elm.cpython-310.pyc,, +pygments/lexers/__pycache__/elpi.cpython-310.pyc,, +pygments/lexers/__pycache__/email.cpython-310.pyc,, +pygments/lexers/__pycache__/erlang.cpython-310.pyc,, +pygments/lexers/__pycache__/esoteric.cpython-310.pyc,, +pygments/lexers/__pycache__/ezhil.cpython-310.pyc,, +pygments/lexers/__pycache__/factor.cpython-310.pyc,, +pygments/lexers/__pycache__/fantom.cpython-310.pyc,, +pygments/lexers/__pycache__/felix.cpython-310.pyc,, +pygments/lexers/__pycache__/fift.cpython-310.pyc,, +pygments/lexers/__pycache__/floscript.cpython-310.pyc,, +pygments/lexers/__pycache__/forth.cpython-310.pyc,, +pygments/lexers/__pycache__/fortran.cpython-310.pyc,, +pygments/lexers/__pycache__/foxpro.cpython-310.pyc,, +pygments/lexers/__pycache__/freefem.cpython-310.pyc,, +pygments/lexers/__pycache__/func.cpython-310.pyc,, +pygments/lexers/__pycache__/functional.cpython-310.pyc,, +pygments/lexers/__pycache__/futhark.cpython-310.pyc,, +pygments/lexers/__pycache__/gcodelexer.cpython-310.pyc,, +pygments/lexers/__pycache__/gdscript.cpython-310.pyc,, +pygments/lexers/__pycache__/go.cpython-310.pyc,, +pygments/lexers/__pycache__/grammar_notation.cpython-310.pyc,, +pygments/lexers/__pycache__/graph.cpython-310.pyc,, +pygments/lexers/__pycache__/graphics.cpython-310.pyc,, +pygments/lexers/__pycache__/graphviz.cpython-310.pyc,, +pygments/lexers/__pycache__/gsql.cpython-310.pyc,, +pygments/lexers/__pycache__/haskell.cpython-310.pyc,, +pygments/lexers/__pycache__/haxe.cpython-310.pyc,, +pygments/lexers/__pycache__/hdl.cpython-310.pyc,, +pygments/lexers/__pycache__/hexdump.cpython-310.pyc,, +pygments/lexers/__pycache__/html.cpython-310.pyc,, +pygments/lexers/__pycache__/idl.cpython-310.pyc,, +pygments/lexers/__pycache__/igor.cpython-310.pyc,, +pygments/lexers/__pycache__/inferno.cpython-310.pyc,, +pygments/lexers/__pycache__/installers.cpython-310.pyc,, +pygments/lexers/__pycache__/int_fiction.cpython-310.pyc,, +pygments/lexers/__pycache__/iolang.cpython-310.pyc,, +pygments/lexers/__pycache__/j.cpython-310.pyc,, +pygments/lexers/__pycache__/javascript.cpython-310.pyc,, +pygments/lexers/__pycache__/jmespath.cpython-310.pyc,, +pygments/lexers/__pycache__/jslt.cpython-310.pyc,, +pygments/lexers/__pycache__/jsonnet.cpython-310.pyc,, +pygments/lexers/__pycache__/julia.cpython-310.pyc,, +pygments/lexers/__pycache__/jvm.cpython-310.pyc,, +pygments/lexers/__pycache__/kuin.cpython-310.pyc,, +pygments/lexers/__pycache__/lilypond.cpython-310.pyc,, +pygments/lexers/__pycache__/lisp.cpython-310.pyc,, +pygments/lexers/__pycache__/macaulay2.cpython-310.pyc,, +pygments/lexers/__pycache__/make.cpython-310.pyc,, +pygments/lexers/__pycache__/markup.cpython-310.pyc,, +pygments/lexers/__pycache__/math.cpython-310.pyc,, +pygments/lexers/__pycache__/matlab.cpython-310.pyc,, +pygments/lexers/__pycache__/maxima.cpython-310.pyc,, +pygments/lexers/__pycache__/meson.cpython-310.pyc,, +pygments/lexers/__pycache__/mime.cpython-310.pyc,, +pygments/lexers/__pycache__/minecraft.cpython-310.pyc,, +pygments/lexers/__pycache__/mips.cpython-310.pyc,, +pygments/lexers/__pycache__/ml.cpython-310.pyc,, +pygments/lexers/__pycache__/modeling.cpython-310.pyc,, +pygments/lexers/__pycache__/modula2.cpython-310.pyc,, +pygments/lexers/__pycache__/monte.cpython-310.pyc,, +pygments/lexers/__pycache__/mosel.cpython-310.pyc,, +pygments/lexers/__pycache__/ncl.cpython-310.pyc,, +pygments/lexers/__pycache__/nimrod.cpython-310.pyc,, +pygments/lexers/__pycache__/nit.cpython-310.pyc,, +pygments/lexers/__pycache__/nix.cpython-310.pyc,, +pygments/lexers/__pycache__/oberon.cpython-310.pyc,, +pygments/lexers/__pycache__/objective.cpython-310.pyc,, +pygments/lexers/__pycache__/ooc.cpython-310.pyc,, +pygments/lexers/__pycache__/other.cpython-310.pyc,, +pygments/lexers/__pycache__/parasail.cpython-310.pyc,, +pygments/lexers/__pycache__/parsers.cpython-310.pyc,, +pygments/lexers/__pycache__/pascal.cpython-310.pyc,, +pygments/lexers/__pycache__/pawn.cpython-310.pyc,, +pygments/lexers/__pycache__/perl.cpython-310.pyc,, +pygments/lexers/__pycache__/phix.cpython-310.pyc,, +pygments/lexers/__pycache__/php.cpython-310.pyc,, +pygments/lexers/__pycache__/pointless.cpython-310.pyc,, +pygments/lexers/__pycache__/pony.cpython-310.pyc,, +pygments/lexers/__pycache__/praat.cpython-310.pyc,, +pygments/lexers/__pycache__/procfile.cpython-310.pyc,, +pygments/lexers/__pycache__/prolog.cpython-310.pyc,, +pygments/lexers/__pycache__/promql.cpython-310.pyc,, +pygments/lexers/__pycache__/python.cpython-310.pyc,, +pygments/lexers/__pycache__/q.cpython-310.pyc,, +pygments/lexers/__pycache__/qlik.cpython-310.pyc,, +pygments/lexers/__pycache__/qvt.cpython-310.pyc,, +pygments/lexers/__pycache__/r.cpython-310.pyc,, +pygments/lexers/__pycache__/rdf.cpython-310.pyc,, +pygments/lexers/__pycache__/rebol.cpython-310.pyc,, +pygments/lexers/__pycache__/resource.cpython-310.pyc,, +pygments/lexers/__pycache__/ride.cpython-310.pyc,, +pygments/lexers/__pycache__/rita.cpython-310.pyc,, +pygments/lexers/__pycache__/rnc.cpython-310.pyc,, +pygments/lexers/__pycache__/roboconf.cpython-310.pyc,, +pygments/lexers/__pycache__/robotframework.cpython-310.pyc,, +pygments/lexers/__pycache__/ruby.cpython-310.pyc,, +pygments/lexers/__pycache__/rust.cpython-310.pyc,, +pygments/lexers/__pycache__/sas.cpython-310.pyc,, +pygments/lexers/__pycache__/savi.cpython-310.pyc,, +pygments/lexers/__pycache__/scdoc.cpython-310.pyc,, +pygments/lexers/__pycache__/scripting.cpython-310.pyc,, +pygments/lexers/__pycache__/sgf.cpython-310.pyc,, +pygments/lexers/__pycache__/shell.cpython-310.pyc,, +pygments/lexers/__pycache__/sieve.cpython-310.pyc,, +pygments/lexers/__pycache__/slash.cpython-310.pyc,, +pygments/lexers/__pycache__/smalltalk.cpython-310.pyc,, +pygments/lexers/__pycache__/smithy.cpython-310.pyc,, +pygments/lexers/__pycache__/smv.cpython-310.pyc,, +pygments/lexers/__pycache__/snobol.cpython-310.pyc,, +pygments/lexers/__pycache__/solidity.cpython-310.pyc,, +pygments/lexers/__pycache__/sophia.cpython-310.pyc,, +pygments/lexers/__pycache__/special.cpython-310.pyc,, +pygments/lexers/__pycache__/spice.cpython-310.pyc,, +pygments/lexers/__pycache__/sql.cpython-310.pyc,, +pygments/lexers/__pycache__/srcinfo.cpython-310.pyc,, +pygments/lexers/__pycache__/stata.cpython-310.pyc,, +pygments/lexers/__pycache__/supercollider.cpython-310.pyc,, +pygments/lexers/__pycache__/tal.cpython-310.pyc,, +pygments/lexers/__pycache__/tcl.cpython-310.pyc,, +pygments/lexers/__pycache__/teal.cpython-310.pyc,, +pygments/lexers/__pycache__/templates.cpython-310.pyc,, +pygments/lexers/__pycache__/teraterm.cpython-310.pyc,, +pygments/lexers/__pycache__/testing.cpython-310.pyc,, +pygments/lexers/__pycache__/text.cpython-310.pyc,, +pygments/lexers/__pycache__/textedit.cpython-310.pyc,, +pygments/lexers/__pycache__/textfmts.cpython-310.pyc,, +pygments/lexers/__pycache__/theorem.cpython-310.pyc,, +pygments/lexers/__pycache__/thingsdb.cpython-310.pyc,, +pygments/lexers/__pycache__/tlb.cpython-310.pyc,, +pygments/lexers/__pycache__/tnt.cpython-310.pyc,, +pygments/lexers/__pycache__/trafficscript.cpython-310.pyc,, +pygments/lexers/__pycache__/typoscript.cpython-310.pyc,, +pygments/lexers/__pycache__/ul4.cpython-310.pyc,, +pygments/lexers/__pycache__/unicon.cpython-310.pyc,, +pygments/lexers/__pycache__/urbi.cpython-310.pyc,, +pygments/lexers/__pycache__/usd.cpython-310.pyc,, +pygments/lexers/__pycache__/varnish.cpython-310.pyc,, +pygments/lexers/__pycache__/verification.cpython-310.pyc,, +pygments/lexers/__pycache__/web.cpython-310.pyc,, +pygments/lexers/__pycache__/webassembly.cpython-310.pyc,, +pygments/lexers/__pycache__/webidl.cpython-310.pyc,, +pygments/lexers/__pycache__/webmisc.cpython-310.pyc,, +pygments/lexers/__pycache__/wgsl.cpython-310.pyc,, +pygments/lexers/__pycache__/whiley.cpython-310.pyc,, +pygments/lexers/__pycache__/wowtoc.cpython-310.pyc,, +pygments/lexers/__pycache__/wren.cpython-310.pyc,, +pygments/lexers/__pycache__/x10.cpython-310.pyc,, +pygments/lexers/__pycache__/xorg.cpython-310.pyc,, +pygments/lexers/__pycache__/yang.cpython-310.pyc,, +pygments/lexers/__pycache__/zig.cpython-310.pyc,, +pygments/lexers/_ada_builtins.py,sha256=Z5IiSpdugeKU4H-SEczWPYG6EO0e-R7lwP7thfORooU,1543 +pygments/lexers/_asy_builtins.py,sha256=qn1PmuijZ0P5Mkd1hWmH1arUt03wGWR7QNZf7ACBcCQ,27287 +pygments/lexers/_cl_builtins.py,sha256=n0Y1jei9ssrZ58Ev4GMxDTspYXU58TyAhLJzBApLttE,13994 +pygments/lexers/_cocoa_builtins.py,sha256=LvaPwfjM2r73yJwTMNu1SiDKzWWqzhJBjN_kivnYsyo,105182 +pygments/lexers/_csound_builtins.py,sha256=IY2H0Eg8iBFdmWwecXGwDC41Glrw_bWLenQCtp6DHzg,18414 +pygments/lexers/_css_builtins.py,sha256=IuSVV88A8b37-XsXGF4_6P6XF5Wv3wd3SxYOS1fOGyU,12446 +pygments/lexers/_julia_builtins.py,sha256=evrvUJjAKN8bCeBooXqAeGnWRc0qBqG_W2TMRPWq7Q4,11883 +pygments/lexers/_lasso_builtins.py,sha256=9Ld87CsxAB9UTV77I9W6s_bv4p-kc2grOnc8Ew7cy3s,134510 +pygments/lexers/_lilypond_builtins.py,sha256=rVmINQYq3iQ9-5W_jGrwW4Q9OX_zEX37I-pn5i8Ya1c,108094 +pygments/lexers/_lua_builtins.py,sha256=x8gSOJzBlwMFH9XFxwgF0-3kMn066YRQTLJyM_fl22s,8116 +pygments/lexers/_mapping.py,sha256=Wf3eD6HwNtDw-5X2jI31IG9bfiUhH1SBzC0SLum_qlM,65633 +pygments/lexers/_mql_builtins.py,sha256=j2K4s3Ye_tYEfJEyUKxvqR3ns_ZOLHGaiPDhkGRk40k,24713 +pygments/lexers/_mysql_builtins.py,sha256=8LqPsM38R2Et1U8sg-oy09aedb-jO1DgVGmssDUw6kg,25842 +pygments/lexers/_openedge_builtins.py,sha256=ZK0Vwj0PfCtg5WpOXCvI0OUnozrNbQxY_HNB_JhAXQc,49398 +pygments/lexers/_php_builtins.py,sha256=ns_E12wFe4Ce3EP38B0DRhAnhHTrdh7WfhjnDWgnJJ4,107930 +pygments/lexers/_postgres_builtins.py,sha256=LQbzTa5ICPqTspfBk7_4JV6bkjB5lz7uuWAUNRj6W4E,13355 +pygments/lexers/_qlik_builtins.py,sha256=zc-fq_8mXsR_XVzTEXFRjGbuwdACJ3O9QrMiEGfuKxQ,12595 +pygments/lexers/_scheme_builtins.py,sha256=ZdZg6-MK7Zt2qqOSBUS5D9YzULtlJIuUxvk39r-HTNc,32564 +pygments/lexers/_scilab_builtins.py,sha256=ib-wZROxsKBi-csMVlGBFHpzDzkT0FPn2wZZN29_9Rw,52413 +pygments/lexers/_sourcemod_builtins.py,sha256=nV2ZcNTvMY7x4oQ2niSNV1XTH9bkXbcAt2-A6ODs55k,26781 +pygments/lexers/_stan_builtins.py,sha256=gl_po01tnvV_tGj31jKHqaSnybhow9ElvFjYU4vWdIs,13445 +pygments/lexers/_stata_builtins.py,sha256=hIGKIkcalQetIwOoN30uflOYgYIOlG3mJ6H6f82edro,27227 +pygments/lexers/_tsql_builtins.py,sha256=wDKfDkftAUjPQW1RPjAwsaLE6VZeTCyCy21z44Uz7Vc,15460 +pygments/lexers/_usd_builtins.py,sha256=H7AYHzkaizd4mFrMtjvI913cX9YdK4qs9JrOlIdcl7c,1658 +pygments/lexers/_vbscript_builtins.py,sha256=EhzEYIk-DBIuU0sMg_davSSL1w-o3enlT1S9uzljZfM,4225 +pygments/lexers/_vim_builtins.py,sha256=BG929NCy6g3LHhpj8R_ueUWqLbuIt0kyCJhxR1Q5SJo,57066 +pygments/lexers/actionscript.py,sha256=lVrwbX2OYnN-KMGd4j3y0z-bWHdNxDDcf8peFMo9x2E,11676 +pygments/lexers/ada.py,sha256=3PAvDXPLpvlpLCSSVl3k1RcBPm7M2LzI3ZU1MJDPqD4,5320 +pygments/lexers/agile.py,sha256=C5m07vIsCir2Amrv0QK0Ah1HbTcjBOBwMdF-x2r__6E,876 +pygments/lexers/algebra.py,sha256=ns54jf39mw-oh8gFZ0mj9Z2zYK98Rq5XJfA99-EjmTE,9873 +pygments/lexers/ambient.py,sha256=u333k5mScy3cUUDQEJdxMQvOEX16aqpTd1DiJeTYzqE,2606 +pygments/lexers/amdgpu.py,sha256=f46YvzV1sD3eEfcdkRYIjkS1YTtHyVJT5T2awL48dlo,1670 +pygments/lexers/ampl.py,sha256=lfHAA0ZG4Dv38l5wTYi6InlXXXJ65FaXLjZU0tR_sWw,4177 +pygments/lexers/apdlexer.py,sha256=EbgslrWGNFYOa8a-fN-PMv4_pCv3Kbdo7TIHN_DCXAg,30766 +pygments/lexers/apl.py,sha256=f4QwKpfzMiUPRXCzmcBfN99TV2YVKyNzZ09Ey0bubYc,3405 +pygments/lexers/archetype.py,sha256=pps0tkPaK1VPJePJg0HyaGfTTwEpApgJ-qg9MJ9a6yE,11469 +pygments/lexers/arrow.py,sha256=wnI3F9Zo9HaYImF2pgksdosy3hkztFyA5GGycbtQoIM,3565 +pygments/lexers/arturo.py,sha256=wIFytTnZAkKVtHVjKK5FK2XLRie_hcv8YSs1Z2UAFlA,11417 +pygments/lexers/asc.py,sha256=2UWRy1MEjFFAEJhnRrbnNLkuJPxkX01rS5csR8h1kqI,1621 +pygments/lexers/asm.py,sha256=2sSPg4tLOPzUTQoTAh8pL5tWbdS_KmqQSr9BTQLVRt0,41243 +pygments/lexers/automation.py,sha256=Zgal7cKSlj0SaHAXx98gHjNM1tvCVIO2UykseFquNy4,19815 +pygments/lexers/bare.py,sha256=0WEBxv-FSgow1RFZzaXGFo00iwklIF5FpjJZDOOWbDo,3021 +pygments/lexers/basic.py,sha256=cxZ7oKq924j8_gDolL_IDV4CSN2vXKk6S7DIw2WlTc8,27923 +pygments/lexers/bdd.py,sha256=JsA-W_-oICZKrZS1i7NNYrRmDRVf3iuI1PufUdMkQhU,1652 +pygments/lexers/berry.py,sha256=swc0EY-bEf0wNFUhfvdkgWmP-PXV8PGvhNagqtsUqmA,3211 +pygments/lexers/bibtex.py,sha256=jzuo9lEZHPKzAAMpIeIWkNTr5I1LO-TMn1V9SdyHT1o,4723 +pygments/lexers/boa.py,sha256=bi5ZlPV61Xehq8bmSdFyqt3BN1jC03pB4t5Foa4vKPw,3915 +pygments/lexers/business.py,sha256=-QHRTWCdabWV8SIbKZ1oLH5NQ3-blSPES8jjIbEvD_I,28112 +pygments/lexers/c_cpp.py,sha256=aXnfgewGmhXwK0-Ii6prW8qT1bJdBOMnDU_C6_d6exU,17791 +pygments/lexers/c_like.py,sha256=uWObWqxC4d-ZyyzEG2yajKZgpq4iINyAcqw7LMnnxZU,29206 +pygments/lexers/capnproto.py,sha256=fE3wK-3ZBzPsDaFf9EkE1dnGDRhJbcCqjyBXRm8GlD4,2175 +pygments/lexers/carbon.py,sha256=HA5UMPQnDa55KCqU9dm6cuvqBuEgb9ZAPv2bCV7K1SE,3231 +pygments/lexers/cddl.py,sha256=PZS6611__hY36NZj8n9Q__JMXNLSm_aMWLpC51axc-M,5182 +pygments/lexers/chapel.py,sha256=1oFt0h0Rh7JnF2qw1QtT0XzkUanV-Xb9Xi9B9ZcHaIA,5157 +pygments/lexers/clean.py,sha256=afJe0orU7Ux_pmmrbSNYPLrTCkY_z0MJTmhUBqPtyJU,6395 +pygments/lexers/comal.py,sha256=9IwxfxVBVMn7Uj2xo-WCr7RhLRDrDe6wjTdAi5hWT2g,3156 +pygments/lexers/compiled.py,sha256=xv8krgoDgd07QDXVPaa5uip1kl9tBprkGT1K5-DwiWE,1407 +pygments/lexers/configs.py,sha256=PChZPzlJBbeeIPPF2soAVbH7GZ653CZMPl7lOe016U0,42338 +pygments/lexers/console.py,sha256=17k4Ngrs0EsaJg90ud6dle-I1SvjoOiYJ-xuGpLesTI,4148 +pygments/lexers/cplint.py,sha256=HrKuhRKjK-atw1FKT_zu4FJjlHOM5BppRzoxisXbpZM,1390 +pygments/lexers/crystal.py,sha256=W7v6oZtbm-aqiM9IOqIypKkudxQICWRdoy-ZyWUdt6s,15756 +pygments/lexers/csound.py,sha256=yuzpCdZuHDmr2eaNodglDrCgX1W62xtQvjitnHlXC6s,16994 +pygments/lexers/css.py,sha256=lZKXESVwZRYTjjRkyB4cYpXk9CHcVxU_embzFRtPhhY,25322 +pygments/lexers/d.py,sha256=f1dFv2mAXqLKSJQ59m66u1Mj9us3fDskxltlrJ8EBmM,9875 +pygments/lexers/dalvik.py,sha256=0Ii9KIUWT5FkJkLKuUcHoHPhav4eEHqsWZL2_ikP1ZU,4607 +pygments/lexers/data.py,sha256=86RLrfLKYxNhjE_1eTjiz0HJxY-393QyyEC37lN98bk,26940 +pygments/lexers/dax.py,sha256=od3D5zfaryI5MO93E_OA-TUpekAcjXv73l6kNit57oI,8099 +pygments/lexers/devicetree.py,sha256=4GKiuZC5bYwG_gIl30fZH8VV2zo4cqwdemQrQgWH7ZQ,4020 +pygments/lexers/diff.py,sha256=VAyAFRFmyMH865rEJ37I3TpyrpLUoEMU95durw6HLRc,5278 +pygments/lexers/dotnet.py,sha256=VjJWh7vslMNIoQdPFNUlAZI0ExuLFV6EdrywYaIjqQU,37623 +pygments/lexers/dsls.py,sha256=iSiDn_HsIeiYx8PFUTai5KHzISWLbClz2hwQudFrgmg,36774 +pygments/lexers/dylan.py,sha256=kTowexiIoOnu-xJ9evkuMBc_7Yr1vxzMITbsCcM_WdI,10380 +pygments/lexers/ecl.py,sha256=Cezl6tWf-a8hMEEUfJqYI3EPf6sSS3kRz0GiOuFIQ4Y,6372 +pygments/lexers/eiffel.py,sha256=jt_49tqubN-gLnobCJn10oWxkCW5MosbUBtm5XjnaI4,2690 +pygments/lexers/elm.py,sha256=OpKdJV4nlVGBv8G8lDeeLdQPbDzaJWtn-TJmS8N9N5w,3152 +pygments/lexers/elpi.py,sha256=3_dxhdKG3yO7yeK4r52XmDt6-UZ2vEuI7FZe5jTcSlg,6370 +pygments/lexers/email.py,sha256=EB3YvRDEP6_B6B1wzq0vS4_zrBX52zdyXxiNI3KHgxs,4742 +pygments/lexers/erlang.py,sha256=EhFx_0D9w85DCJR9FyUprFPiaDvJps3xy-Kn4whWcjs,19170 +pygments/lexers/esoteric.py,sha256=V--w2W-Jyl4yB8A00f4GikVeZXe_5lWHE0RMnfuoLTY,10396 +pygments/lexers/ezhil.py,sha256=xu9wMEESVWCLgNitSS0IS855TSJ3Yb2KXBrxmYqHKSo,3273 +pygments/lexers/factor.py,sha256=iVSIZaT7-qAI41wP__p2XjTSyREBSyAErPLVGP4bMeU,19531 +pygments/lexers/fantom.py,sha256=0DylUHKCeBMfQK4QczavL5ugyPTV0u5H0Lk0YTv6_mU,10197 +pygments/lexers/felix.py,sha256=fJsd0mNaSwkjU32-103-9XDzzz2lj-ONPiW8JkZAqxM,9646 +pygments/lexers/fift.py,sha256=ISH11Bvz15ZsMjTMdLDsHFx2HYgpGyDUlvi1CN8MC8Q,1621 +pygments/lexers/floscript.py,sha256=UYPHl2Ygez7RfBPTU00hUMXrU10mB996vz6rFisAmTs,2668 +pygments/lexers/forth.py,sha256=PzfXkhpusFIGVwrzVsqLgljlybN92OeU0_E3rHup2m0,7194 +pygments/lexers/fortran.py,sha256=i7xVuZEQnYruMo4xfvGI0izozI1qJvcPO8Dr7KRU0Vg,10336 +pygments/lexers/foxpro.py,sha256=XmVcWgw70vh31gri3LhfrB2YQ-RhkUkBKly0xHt8VQs,26212 +pygments/lexers/freefem.py,sha256=e_YjjHZu8PzQBykLzhpY2RJHjCnLxnlmoGj-5RinP6c,26914 +pygments/lexers/func.py,sha256=h6IHCdydWKpQcamC1XAGJjaGK-UDvh9iM0ZuWQK05dQ,3622 +pygments/lexers/functional.py,sha256=IpvPKomRGDqGs_MZZjkX5niMGZO9s5X1Dk0HDyO9zWM,674 +pygments/lexers/futhark.py,sha256=NwNYLxQ1feJwtPjG54VmSmo_ZtcTqtlzr0QQppmxWUM,3732 +pygments/lexers/gcodelexer.py,sha256=H754t2DKkgJYF-mzm4uI9dqc0qE02fjlZZfXM0eg6Lc,826 +pygments/lexers/gdscript.py,sha256=DFvf9eJgJic5DUHt2lrJksSu5X6eXRfrh30t5yAVSzY,7543 +pygments/lexers/go.py,sha256=7SiL-LKgT6BPX44KASMgt4mne5KOusaiAfN13YxHn5g,3761 +pygments/lexers/grammar_notation.py,sha256=SNQNggmjYCC0yRN2m2r_33X2pa8bw0AxLJsG1G5FjdQ,7980 +pygments/lexers/graph.py,sha256=AHtLRCnQdeY5fQdMJItngN67PBVlxWn2Eq_0ua4VlJI,3861 +pygments/lexers/graphics.py,sha256=AqhzeSjpQwk1Vrj_OT5jKDJMonB90Sr01mM3q90WGVg,39026 +pygments/lexers/graphviz.py,sha256=_yJaR2CW--2ljvnPdNjsVOvosPoUJw0pb3Of33nWBtk,1935 +pygments/lexers/gsql.py,sha256=7GDWo1Wvt5UmJhAC8HQND1kbdONBRE1YhdawBzgGrHA,3991 +pygments/lexers/haskell.py,sha256=RkYEh9-amHeRS0-dU5Nyz4RL4KKJz27PsWhNpf-C7NA,32898 +pygments/lexers/haxe.py,sha256=W4ILlVNk1D6USMa2Kkr00IFaoqtUIxd5cO8_xWzo23k,30976 +pygments/lexers/hdl.py,sha256=xA5YnwhgaOTzvLpE1VIP8j7l5LM2FjkpssZyMe3sPtY,22520 +pygments/lexers/hexdump.py,sha256=QLvXzWmo2_xqHMy55OTXUB15CokyQLMzHvmXuwMj6z8,3603 +pygments/lexers/html.py,sha256=MTahzHDswG59eYeNbEwKP16pkY4DV2F2M3Rm-ZDqmWI,19879 +pygments/lexers/idl.py,sha256=bhS43u_ycIVN-dxlL0rkVDW0Kunuv4nCgMiw4CrEVTk,15450 +pygments/lexers/igor.py,sha256=lwYCukIE6R4Ly5WAFcjs03m5T7HNK9FpXlYjoJg33gY,30631 +pygments/lexers/inferno.py,sha256=0pRpoR7W1Kp8q_9mxY43rYw_RYef3ruJ6xV1LwYbz3A,3136 +pygments/lexers/installers.py,sha256=5_jhlzWYi_6LMEr8BnpbjgiScZ_G3Q7AH6fTG4WrPRc,13178 +pygments/lexers/int_fiction.py,sha256=CkGFrMPiZZ6aQ607E4yfZDTcYpz1pdrbd4azyx7XVX8,57119 +pygments/lexers/iolang.py,sha256=ieom-sc8xdXbHi4g5LDofvLduKZdVBSOEwXocKmQU2g,1906 +pygments/lexers/j.py,sha256=Kd0Rga_1uwU_YhfXebvGuHKpOXM2SVH0_x_CqkCJVCk,4854 +pygments/lexers/javascript.py,sha256=y4ve9s2jq4UaTr9PhkNERIlu2DpbP_l64N_r2JJOBV0,62859 +pygments/lexers/jmespath.py,sha256=3P4_YQXh4IoohDQhoh0pquyFuUHVd88g7KYmVYg3NoM,2059 +pygments/lexers/jslt.py,sha256=cvH2LIuUnkhaqhfWvkIjS7sSx2FpXSEvl3rOF47d6Y0,3701 +pygments/lexers/jsonnet.py,sha256=CiJT00Tp0lBg9j_1EW-d6_poumtHoP2TmEQVDlf3cTs,5635 +pygments/lexers/julia.py,sha256=F5zuRm7OSZQeV1na7lasypEDGreLAzOdfaKmH1HIxgE,11646 +pygments/lexers/jvm.py,sha256=nyRvIF12oTZRmU0qDhgOQx8AsUgoZj1zMU8tDGyOP8U,72929 +pygments/lexers/kuin.py,sha256=AVUDd6JAy1jClIYeQDPcV-O7xGfb7x7fojN7hb7ANHU,11406 +pygments/lexers/lilypond.py,sha256=_A5P7PaITP8COXG8dy9nS7CzfdKY2UOe1XLGtucuip4,9753 +pygments/lexers/lisp.py,sha256=lIeqd1varBR8dCUi3MNr60Ikxf4W9sFsF7KW6n0lrR4,144039 +pygments/lexers/macaulay2.py,sha256=xc7ZUhQhfnRIoRBnWIbVw89KmZHac7qO1eQm2lSvnWA,31914 +pygments/lexers/make.py,sha256=Pif0_6zvfwAYd3cm2oKMDJHdySP2T7CzHZPFnGMmboI,7618 +pygments/lexers/markup.py,sha256=A_9ldnhEXDByWZL77eTq-JdyLnF_KAJ0by7jjzU70G4,58129 +pygments/lexers/math.py,sha256=Wl6lU1VWevtQXymt6jdDeuz9LEMiPKl1On3Zm3QXQ8M,676 +pygments/lexers/matlab.py,sha256=WplG578_mB6sHlDDfgY83SMadCcqD_sN5Jb593rSr6w,132852 +pygments/lexers/maxima.py,sha256=tLSjeDIBZKHRoMludgF7oBdz1xTAHyvIEG6BshyBXyM,2716 +pygments/lexers/meson.py,sha256=ij0CgbEULfqLMcY_BQ7PxmCwWOYLc9XZwtrALFSinEA,4337 +pygments/lexers/mime.py,sha256=-wj3pvAQzNTC8fTZk9AV4Q-LIQhMUocet-j2kqGj5gs,7538 +pygments/lexers/minecraft.py,sha256=-Tb9aNbNM8gXgq69uaCxfmyTQlJ9dYYTK9UaFbSE2CI,13846 +pygments/lexers/mips.py,sha256=JBfvOPp5HTGp6b0kynkjzuJOTRAdTsJJdjPWLAtu1fo,4604 +pygments/lexers/ml.py,sha256=WlIdrpHCP8Z8OB5nkDXA6NyTvxPuc0tOueobOc8V840,35324 +pygments/lexers/modeling.py,sha256=3KxuIs1-lb_FN921kCCgjq84R69ATEp6K1EQyWaxp7M,13524 +pygments/lexers/modula2.py,sha256=z-4QbG4Bgjd-0ONC9SadglqCW2QmIMl-60b_n35V-70,53073 +pygments/lexers/monte.py,sha256=pKPKOkwxcGzJLVisA-H7xJJW9JUh2SsXyDi32mF5nSQ,6290 +pygments/lexers/mosel.py,sha256=_JeaaK5Jn0r7JcuLl62u1sl6uInkrP9g4EosCf9Z-IQ,9187 +pygments/lexers/ncl.py,sha256=O6CckyU8yABKmx8xmj4q6E2isWORmvNO7Xl1JX5NwVQ,63962 +pygments/lexers/nimrod.py,sha256=O_8dxk9x3vlYFPYjpe8iqQcCrcZmNnBE_Ld1lzO7xVI,6416 +pygments/lexers/nit.py,sha256=Ghja_AX8DIg_KA-rmvgmL76kZI-lq3pARMiebYi9Nqw,2726 +pygments/lexers/nix.py,sha256=qeKFMOSHa627SlkPsbpIOfzLu2ej-JdgGkDAqt4isP4,4015 +pygments/lexers/oberon.py,sha256=LgYgzGPus7w1lJLUAu2cmIRQGEgQiid34QH5Upa7Uro,4169 +pygments/lexers/objective.py,sha256=VoUeZYa1aO5rU69dDpv4Ob5FOYyr3nm-eJ3m0kuti0U,22961 +pygments/lexers/ooc.py,sha256=BEj7kfOimF_Qi62R-B8bmYGy_5QIE1jS20NfrnNE9Ss,2982 +pygments/lexers/other.py,sha256=oXVtTELCPFxwuJxN5j_iYrt0Q9Aonq-GkotncUFPpeE,1744 +pygments/lexers/parasail.py,sha256=MxO40OTg_5z_EnMyb6weQHjhtzL1WgSwfjCXc0zl82Y,2720 +pygments/lexers/parsers.py,sha256=JiQT3IMsf2f0JLbiDpupZNSsHnMUH57OoAr0D_3AvbY,25904 +pygments/lexers/pascal.py,sha256=EHZi_mh4V7LuGXtcbTQnrrfqDDXcGyVwDvJmN8XRcps,30880 +pygments/lexers/pawn.py,sha256=fNq73VwQoXeKiRFKYIKzzOClcUb2eLPJIhymlnxKZlg,8146 +pygments/lexers/perl.py,sha256=82_7saaR3qBFlSnWYyEtkir7uvXTeUZqFUhjdkdTTXs,39170 +pygments/lexers/phix.py,sha256=48JEQK_0ijjipwDl1F-ik0B_CGUMsZOvXlvXzIqHVmk,23252 +pygments/lexers/php.py,sha256=b4sVWZTMq0t2GMlaFmzYWJna9WuDQFIm-vdqXkb89xo,13040 +pygments/lexers/pointless.py,sha256=dUATlYVUZkbggQ6PUymDKhB0UKqDo-KCf0Fp_EjWsTM,1975 +pygments/lexers/pony.py,sha256=k03DAhS-_G8SZsfXZOjF5YkQaMQ28_uy68_CMaxuUC4,3244 +pygments/lexers/praat.py,sha256=rTl5GSoJ58LQxRf_Ek0A1H4iCm2IVQFxEwSvbMki7tw,12677 +pygments/lexers/procfile.py,sha256=naQZcdvxVUjT56R0U6_YpgNCgfmNE9Xjzx9uQesl-Cg,1156 +pygments/lexers/prolog.py,sha256=d010T34HBjbf8I1PoCiU2mDPQlMLhBG8-eFbAILqy7Q,12351 +pygments/lexers/promql.py,sha256=Qw0VXIJGRkw6ZiSzvtR0KFiWmPuyI9xxXgDl_l3cHmc,4715 +pygments/lexers/python.py,sha256=Dxdo6Pej2cdLI-LdR7jw18NQegVp3_YQIgVNV2jbOWU,53376 +pygments/lexers/q.py,sha256=Vw36x6rIp2U1b8GUtUbMlcD0MVqW3unCZEMBEt-PflQ,6932 +pygments/lexers/qlik.py,sha256=gCAQCe1Tuy_cMI4n51Sb8fG50L73AgmUMEWMmZRHeJg,3665 +pygments/lexers/qvt.py,sha256=IjHyopkDochlQt_fIf7Mrxw84pIg-gj4BkDHtEiKb_U,6072 +pygments/lexers/r.py,sha256=xMSNpNN3Hm0W51-TEfvzPeOGtobM2nIjGVmIoVydlfU,6185 +pygments/lexers/rdf.py,sha256=9aVmpa5FSE3rZwekkSRYp-qxi4Yg_SlFbX5kIPjgQdU,15790 +pygments/lexers/rebol.py,sha256=TMT3JlXoWvJbZwZdLuekwgv584HEP1mfsD2g9Oqj0IU,18248 +pygments/lexers/resource.py,sha256=KxxQJi9NPmghpa1mekxUFBd8eaiSB9pV_F4Eds4kK0k,2902 +pygments/lexers/ride.py,sha256=0mT89oluoz5lUT8ar6uPiSwTIBwyMc6oHYnrM5QY7Vo,5056 +pygments/lexers/rita.py,sha256=gPbMhlCyS_ekzXIKboXGEmtGZlSSUx0k6qcuWEHuM6s,1128 +pygments/lexers/rnc.py,sha256=SjXKJWNifTTWQC9NlcWNmdhkln0REEpEvaY6nVy6n4w,1973 +pygments/lexers/roboconf.py,sha256=HY0dSPJxg7knQ-cM6YotX8EoVjuQi-LeKYKeWpQyF94,1962 +pygments/lexers/robotframework.py,sha256=MJ4cnUDZWDrXCpYi-kJLqe1HpzJ24DMtNswDeuSnVCc,18449 +pygments/lexers/ruby.py,sha256=74k_3VNHUiJGVbqdAI9wALjir3CjpjhJiGM1gPir8iU,22775 +pygments/lexers/rust.py,sha256=Oq5bFgf_hcOLxiBLhzPArUFXgg3Ude_xpeKPC3g-HDI,8216 +pygments/lexers/sas.py,sha256=u0vkPLehX7UERsKIkooC4IFJdTf44L2_Qqdpx1De7wk,9400 +pygments/lexers/savi.py,sha256=3A563B-Wzp5AGZG0ruSQiPbe-c3uYGwCPUmnPJSEKOk,4645 +pygments/lexers/scdoc.py,sha256=cOeE0NUl7_PK9U6UwPMpoSfeFgyG5qIgJyw8oM7Qa-g,2239 +pygments/lexers/scripting.py,sha256=EkXKzWLg-jpsV8rep2YW6fDsCDz-d3fY1qv1QVE7MI8,70014 +pygments/lexers/sgf.py,sha256=Oa3SNL31wN8rhY0gby61kbPxttvRQJTRN38CSMUiMJE,1986 +pygments/lexers/shell.py,sha256=oC115DNmEzPYPdKN5jNUz-gV44c_qVb2Y5OfGO3gQ_U,36466 +pygments/lexers/sieve.py,sha256=lLcCLdvj-AeudiG2xq2zTN16v0zSEXK5vcr1keg0TxA,2441 +pygments/lexers/slash.py,sha256=_soPrmF1ylAw7IWfRHTMMUdifwy4oNV5FpYMBQsVSyM,8482 +pygments/lexers/smalltalk.py,sha256=9UNqjCWOay3nb5BVqN2Muw-Ci63WnscW6xJcGnNWVq4,7206 +pygments/lexers/smithy.py,sha256=FMvWXKvjF_hUNqI7BmZYbVRrPg5_AnnzORHPp3skcXs,2660 +pygments/lexers/smv.py,sha256=VyWuTjtAhaZmu6oZG3Om_kU3NhorVz9ivzeu_mfRjwo,2773 +pygments/lexers/snobol.py,sha256=Wp7ViMnddy79tWJjsgSB9kIPFh_OhaM4Y2tZjWDuvMI,2732 +pygments/lexers/solidity.py,sha256=Mhmypb5WzxGdBaQpySae9NbiFJllkEezditjHajd7RA,3127 +pygments/lexers/sophia.py,sha256=0gARv0DbMi7DWUc793pd4f7IKxSeOlkjk_eez8tLihs,3330 +pygments/lexers/special.py,sha256=SDWqBKmWmKyvi8VNfPpW1V4g7ergad4RfUumDh6bNrk,3414 +pygments/lexers/spice.py,sha256=2cE4npHPtc2tsIrI8rTbSZt4uRovx5H1YeKQZFL2d3s,2733 +pygments/lexers/sql.py,sha256=j-3EXShmUyB30w7zAfO8uv0ojK8ycQhq6gc94aQOz7I,42086 +pygments/lexers/srcinfo.py,sha256=x_6DsZ0mKVbI_v_G5bLBQl0WyFwj3tXmOqfHIB6zve4,1693 +pygments/lexers/stata.py,sha256=-IRfLl_rpuOiKs0Vnpc6OEKuA6AXbklW6RBGc7xzGEM,6416 +pygments/lexers/supercollider.py,sha256=Ua4RbmCRYPz7PcT1uyloAbfKc9kZim7nAKr55E26scY,3698 +pygments/lexers/tal.py,sha256=_wLncXDRKsBDQQYkUkq4tGGNjN5oj3VFhqoRZUcBYSE,2639 +pygments/lexers/tcl.py,sha256=STjHSlVeWo-Eg4tKqn_7m3_3efcmHP0WCoL1_PK5tt0,5513 +pygments/lexers/teal.py,sha256=t8eOzrIiImVuUtkBlSPl9Y4olrCAZ21SQB-T67WZasQ,3523 +pygments/lexers/templates.py,sha256=Eawt5MXOe1V2RB_vhp6097GQMjoKHPSEWHKifhdMazw,72610 +pygments/lexers/teraterm.py,sha256=71NiLefy40L6R4B-qy-ho8Uy4kAQqsi7iLxexCDS5rU,9719 +pygments/lexers/testing.py,sha256=Ue93uh36l7JKc0cUDnLmk-E221b5y7qt6xXHvaJRxt4,10767 +pygments/lexers/text.py,sha256=ntF8QwsuhsqyQdxMB6H7MYP7o8A7xSxHe0UMTZLKSK8,1029 +pygments/lexers/textedit.py,sha256=xVU-3K5-0KEkb0pJ4hJjrjpnz9hJaIuLQOxfk9nLWjM,7609 +pygments/lexers/textfmts.py,sha256=DDijMmBwcKGrYW2qbwz8W3vD3Uq6wpX_9w7fYKYnV2M,15192 +pygments/lexers/theorem.py,sha256=o3L_nfHpkio4l5RIj0UQ-CqWUEztt7Q5vh5lqDHZ-Co,20157 +pygments/lexers/thingsdb.py,sha256=FwelchVBXKf3nc78UgA9SyJbHta3EMdrLnQizIxS5SE,4228 +pygments/lexers/tlb.py,sha256=sQCsK3j8zqGVI_KCuursmOr4JIs3sfpqF_wPpQpyKM0,1377 +pygments/lexers/tnt.py,sha256=uvdcsw5NRZjbFouXBET3-w8I7LG9hqzMXiK6s_GK2ZY,10457 +pygments/lexers/trafficscript.py,sha256=GDl_7cSFit1anVH7KNH0qVts1eHcp_GGxpzIYWZabeg,1474 +pygments/lexers/typoscript.py,sha256=nIAUIlbL5sVqpTEt34YdUGDNmu60a7itBYaaUMuzAIs,8207 +pygments/lexers/ul4.py,sha256=XOm5dWiWtWN3RQMpA5VHvl1J1jg014SK27aJnVjq1C0,8956 +pygments/lexers/unicon.py,sha256=4MDnwsgXzLlm0zaDqYr9uHFNspgCzQRNzyxClVQ-RMI,18512 +pygments/lexers/urbi.py,sha256=3CkxfMOqpSVZpOq7_4YrWcmjq4B3CF_IDGIxAsedWQ0,6037 +pygments/lexers/usd.py,sha256=7FiS_KV1Q9Cvf51fAHmMqr5QDz1OtjqTkp1S-8VyQTw,3513 +pygments/lexers/varnish.py,sha256=LYL6rhIw-ApJRT4qkxFaxG71OPbHsiFv4mtfr0m1OPY,7273 +pygments/lexers/verification.py,sha256=1C1ntqvZmrCtzHV9eDIga1LtQ4jpAlb6GnWkNucEJXQ,3885 +pygments/lexers/web.py,sha256=lscit_NgXsHDL8sjvNvAFriPBe24cz0b2rBGFFpYlv8,894 +pygments/lexers/webassembly.py,sha256=HGFp4qMAK0ejx0omrThEtIvl_ArKOeS7b4yZkipqmIo,5699 +pygments/lexers/webidl.py,sha256=VeqO7i3Y-e0ObtLxn1sniGmL_CvOWekaP2XyUFzDlLQ,10517 +pygments/lexers/webmisc.py,sha256=veICtUXL33effcYDX8_FU1F0Q5FfZfzcC2Tyu5SYT9A,40549 +pygments/lexers/wgsl.py,sha256=Mffgj3x5tFwsKWihw2kbhSmhvGNEgQc_pJi6C3mEQxc,11920 +pygments/lexers/whiley.py,sha256=SbbftaItSgIdczzT8qOV8mH1m6dD3aM2EvRk4ila6Co,4018 +pygments/lexers/wowtoc.py,sha256=o_01SGTJ3jecVHEpkhqHdwI66XQVi8wNeajVYPbd4Bo,4021 +pygments/lexers/wren.py,sha256=I3LaIzy3aa0aDXJd9PJp-SiJtgJHktISHdQ_zQuMHLY,3239 +pygments/lexers/x10.py,sha256=p3VwQg70e8ZR2iwR2kY-yojTyD_s3xVJMLTCWrpz6Hc,1920 +pygments/lexers/xorg.py,sha256=rXKVNQiiniNoaM4VTkLFj106NLddCBo1BV45BTxT1HE,902 +pygments/lexers/yang.py,sha256=rx08okYvJgWMe6jMRkt-ysEuaTi85X5DE0Q5EHb-Ehg,4500 +pygments/lexers/zig.py,sha256=B5QZGfEoNkoAG5EZxHk28MZ1n2Q1Gvx11kcvTQ6N5X8,3953 +pygments/modeline.py,sha256=eF2vO4LpOGoPvIKKkbPfnyut8hT4UiebZPpb-BYGQdI,986 +pygments/plugin.py,sha256=k_gtJHBriDDWiNinQ-h_qnDUm7EnPLuXaTGH3Cd5pZw,2579 +pygments/regexopt.py,sha256=jg1ALogcYGU96TQS9isBl6dCrvw5y5--BP_K-uFk_8s,3072 +pygments/scanner.py,sha256=b_nu5_f3HCgSdp5S_aNRBQ1MSCm4ZjDwec2OmTRickw,3092 +pygments/sphinxext.py,sha256=MWLG74mLD1iqqrUbx7ZN6APuZ5eGi88yPbK0zajl3VQ,6816 +pygments/style.py,sha256=ePJAMWuctRHvKhdIFkZT9CAGEdHBZxx-Bv7PURa8NB4,6245 +pygments/styles/__init__.py,sha256=pVdtmEjFYxEO6Lf-duCMbfy_aB_FwXwD9Hzj1otwOow,3676 +pygments/styles/__pycache__/__init__.cpython-310.pyc,, +pygments/styles/__pycache__/abap.cpython-310.pyc,, +pygments/styles/__pycache__/algol.cpython-310.pyc,, +pygments/styles/__pycache__/algol_nu.cpython-310.pyc,, +pygments/styles/__pycache__/arduino.cpython-310.pyc,, +pygments/styles/__pycache__/autumn.cpython-310.pyc,, +pygments/styles/__pycache__/borland.cpython-310.pyc,, +pygments/styles/__pycache__/bw.cpython-310.pyc,, +pygments/styles/__pycache__/colorful.cpython-310.pyc,, +pygments/styles/__pycache__/default.cpython-310.pyc,, +pygments/styles/__pycache__/dracula.cpython-310.pyc,, +pygments/styles/__pycache__/emacs.cpython-310.pyc,, +pygments/styles/__pycache__/friendly.cpython-310.pyc,, +pygments/styles/__pycache__/friendly_grayscale.cpython-310.pyc,, +pygments/styles/__pycache__/fruity.cpython-310.pyc,, +pygments/styles/__pycache__/gh_dark.cpython-310.pyc,, +pygments/styles/__pycache__/gruvbox.cpython-310.pyc,, +pygments/styles/__pycache__/igor.cpython-310.pyc,, +pygments/styles/__pycache__/inkpot.cpython-310.pyc,, +pygments/styles/__pycache__/lilypond.cpython-310.pyc,, +pygments/styles/__pycache__/lovelace.cpython-310.pyc,, +pygments/styles/__pycache__/manni.cpython-310.pyc,, +pygments/styles/__pycache__/material.cpython-310.pyc,, +pygments/styles/__pycache__/monokai.cpython-310.pyc,, +pygments/styles/__pycache__/murphy.cpython-310.pyc,, +pygments/styles/__pycache__/native.cpython-310.pyc,, +pygments/styles/__pycache__/nord.cpython-310.pyc,, +pygments/styles/__pycache__/onedark.cpython-310.pyc,, +pygments/styles/__pycache__/paraiso_dark.cpython-310.pyc,, +pygments/styles/__pycache__/paraiso_light.cpython-310.pyc,, +pygments/styles/__pycache__/pastie.cpython-310.pyc,, +pygments/styles/__pycache__/perldoc.cpython-310.pyc,, +pygments/styles/__pycache__/rainbow_dash.cpython-310.pyc,, +pygments/styles/__pycache__/rrt.cpython-310.pyc,, +pygments/styles/__pycache__/sas.cpython-310.pyc,, +pygments/styles/__pycache__/solarized.cpython-310.pyc,, +pygments/styles/__pycache__/staroffice.cpython-310.pyc,, +pygments/styles/__pycache__/stata_dark.cpython-310.pyc,, +pygments/styles/__pycache__/stata_light.cpython-310.pyc,, +pygments/styles/__pycache__/tango.cpython-310.pyc,, +pygments/styles/__pycache__/trac.cpython-310.pyc,, +pygments/styles/__pycache__/vim.cpython-310.pyc,, +pygments/styles/__pycache__/vs.cpython-310.pyc,, +pygments/styles/__pycache__/xcode.cpython-310.pyc,, +pygments/styles/__pycache__/zenburn.cpython-310.pyc,, +pygments/styles/abap.py,sha256=pvsya8aKMVAkVeAsOumTamTsbnpTSaGNn3AUrYkbN_o,705 +pygments/styles/algol.py,sha256=Zpxg6alN3GogkpUWcp_EJlnqD_CBC9oxsvYkCOHLDHk,2216 +pygments/styles/algol_nu.py,sha256=JyBOI8tzg27aOr5Ua8JNPLi6d4BYTixFdkPBbKqEiAw,2231 +pygments/styles/arduino.py,sha256=Vsg-NzWKZZb4qMxgzE_Y2TX9lSx3FoUppRGUt8CSilk,4443 +pygments/styles/autumn.py,sha256=vW1xxGF7Sa6IQTBlKerrufIXY52O4QMglDazMPb7_Ek,2096 +pygments/styles/borland.py,sha256=hWDB1gJT-_3_exWgJ-49GJh1gwTNrOyQXp53zYer9cM,1514 +pygments/styles/bw.py,sha256=BSDFekRBkr7c156YahNtmJ9gxBwjxIO4J_p6olEB4t0,1308 +pygments/styles/colorful.py,sha256=ZbiSqyr-ZA4T-1RtHauihdJc05-Kt-lSFbJSO_jIwgU,2730 +pygments/styles/default.py,sha256=LSIWGkEJq5VI2qFkCjoDtHrlVPODKHgjADBsmD_kTEQ,2488 +pygments/styles/dracula.py,sha256=w_P_4Khe38U7BuEbbFHTwCGHBDLwAN7L9i6bKMnki48,3314 +pygments/styles/emacs.py,sha256=IpMu1Rlfe7ukZ17loTqy1NBxP-YTpGIjAuss-3lVVq0,2439 +pygments/styles/friendly.py,sha256=e8c1p4PwJDGoaR7myygugbEkP9PoHl1p17w5R1s5xGI,2502 +pygments/styles/friendly_grayscale.py,sha256=WHiGg5n6eN7Nd3TgMfSO_nUzjsulUJG0w9vWbQ6AAzw,2707 +pygments/styles/fruity.py,sha256=RHODyvBNzfoMsCXpo73rVE2P861U5Sp4y4Yeh5S5qA0,1274 +pygments/styles/gh_dark.py,sha256=o8MLrC_CA_RTLr7BoRdrXumBl6CvBEP1fDY_hqElDew,3481 +pygments/styles/gruvbox.py,sha256=N8HjBr8bmiCfqf0H_BSuSFxH9cM-sS7AYmvvK3NMPFE,3230 +pygments/styles/igor.py,sha256=WLVI14RraRiyO570MBPMTboieVvUyCxvToymTW33tec,692 +pygments/styles/inkpot.py,sha256=9zP09w_sdHagTqv2sIQXdZAn9HYCppQtiNKVoLWiSBA,2302 +pygments/styles/lilypond.py,sha256=wh-ip2qKgUTxdh82QKYCPdueDXhnCj-yatftcgWCljo,2016 +pygments/styles/lovelace.py,sha256=fZ8NAWw-4625onSvDxWbZoPRrQ65BrcTjVy3PqS5D30,3117 +pygments/styles/manni.py,sha256=CRsrOSBbQAXpV9JmnkVtHPp_cQIfpLL1tOA_3uG6dM8,2350 +pygments/styles/material.py,sha256=B4bBRcdTo87UGtNcmN4UUmdiFgv2hxacVne-eaJIzLg,4083 +pygments/styles/monokai.py,sha256=iqbl4_RZMUjHUj8jz98m4iB817M1meiiToS26xgP8C4,5063 +pygments/styles/murphy.py,sha256=lhyTqRqpzrICQeIMo74oQQVrfsyvmVpDeCr96xOXAp4,2703 +pygments/styles/native.py,sha256=sJMoL1xHC9o17AGgg2WMJevqxtGXRbYDL87Sipm1tik,1948 +pygments/styles/nord.py,sha256=CFLY6iz19dANPlYGDAWoxcoNAwYCkOGg5Mg7AtepNhU,5244 +pygments/styles/onedark.py,sha256=BgsmXfNk_A4NUpNZYtl0C-Cj368DiLrlrBRlop8CixQ,1664 +pygments/styles/paraiso_dark.py,sha256=cgvn8V7vQXKzPLdThGS-P7YRN1cT08UDxnL0AcKOWMw,5526 +pygments/styles/paraiso_light.py,sha256=QnkhJ-lFcGr0YktkIL2S3Ch8qntu95NWg2R1sAuPGA0,5530 +pygments/styles/pastie.py,sha256=7w75VUNUMoDofO-KHBXCfUZMxynstTDZzkcsStafOdE,2425 +pygments/styles/perldoc.py,sha256=ykm2Qo9YtThYyvAfAMJ2KUNCujSAP6qJbzj57Dv0USI,2128 +pygments/styles/rainbow_dash.py,sha256=Bz6H7VU7nc8cfT_c9s-DLQv4WpSaQV-bCluR371jVGs,2432 +pygments/styles/rrt.py,sha256=PyyasbLgJEGP_P9sMWMSKM2cVhk0yF6p0vGvtMEytG8,874 +pygments/styles/sas.py,sha256=D2HCwKU2ojWy-NPJsjiUKSDKNeMfJ7e04K_Hqki-dWo,1393 +pygments/styles/solarized.py,sha256=rBI99bncVDviqQZpkb7_aUCZ8UW3tIR65UYQzhhC3mA,4078 +pygments/styles/staroffice.py,sha256=SQb2dU8N5iVDK6z7NnYF-0XQL-s2mcu47vcbeImZ2mc,770 +pygments/styles/stata_dark.py,sha256=uKZsvGdSMxfKPlmKvPLWa48C1jXwuxlYVb44Kp2t2ik,1198 +pygments/styles/stata_light.py,sha256=l5V1Sdz31jEkzO7CdSSBXrKWfoGgOShjarO5WtqX4DE,1227 +pygments/styles/tango.py,sha256=1vrbjUD6WJHAkoHHO9emWYycuTB13rBRAPOeHwhzE4c,7039 +pygments/styles/trac.py,sha256=kLK0nHm0nqK-Yu3MnROfR_PnEyhJJhF5RxxqFu4dfaM,1885 +pygments/styles/vim.py,sha256=LoSIHP2S8t9jaKE0apklZkkPVG0EiVSLmI9RwEv1sUU,1922 +pygments/styles/vs.py,sha256=TCDk9S4B5mEFVs5rKz2f6yjb6Qc_2hv8QyII1QCchOc,1026 +pygments/styles/xcode.py,sha256=8nrGsI4FXIPx3DrGoqhLz_U0jZgpgKfW1FA2oV2iJ4A,1453 +pygments/styles/zenburn.py,sha256=RWniXYptEN65ip_FZGHnd25RvujfHSmhSZSWioH0xY8,2148 +pygments/token.py,sha256=seNsmcch9OEHXYirh8Ool7w8xDhfNTbLj5rHAC-gc_o,6184 +pygments/unistring.py,sha256=FaUfG14NBJEKLQoY9qj6JYeXrpYcLmKulghdxOGFaOc,63223 +pygments/util.py,sha256=AEVY0qonyyEMgv4Do2dINrrqUAwUk2XYSqHM650uzek,10230 diff --git a/venv/lib/python3.10/site-packages/Pygments-2.15.1.dist-info/WHEEL b/venv/lib/python3.10/site-packages/Pygments-2.15.1.dist-info/WHEEL new file mode 100644 index 000000000..1f37c02f2 --- /dev/null +++ b/venv/lib/python3.10/site-packages/Pygments-2.15.1.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.40.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/venv/lib/python3.10/site-packages/Pygments-2.15.1.dist-info/entry_points.txt b/venv/lib/python3.10/site-packages/Pygments-2.15.1.dist-info/entry_points.txt new file mode 100644 index 000000000..15498e35f --- /dev/null +++ b/venv/lib/python3.10/site-packages/Pygments-2.15.1.dist-info/entry_points.txt @@ -0,0 +1,2 @@ +[console_scripts] +pygmentize = pygments.cmdline:main diff --git a/venv/lib/python3.10/site-packages/Pygments-2.15.1.dist-info/top_level.txt b/venv/lib/python3.10/site-packages/Pygments-2.15.1.dist-info/top_level.txt new file mode 100644 index 000000000..a9f49e01c --- /dev/null +++ b/venv/lib/python3.10/site-packages/Pygments-2.15.1.dist-info/top_level.txt @@ -0,0 +1 @@ +pygments diff --git a/venv/lib/python3.10/site-packages/__pycache__/decorator.cpython-310.pyc b/venv/lib/python3.10/site-packages/__pycache__/decorator.cpython-310.pyc new file mode 100644 index 000000000..e93d784fc Binary files /dev/null and b/venv/lib/python3.10/site-packages/__pycache__/decorator.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/__pycache__/ghp_import.cpython-310.pyc b/venv/lib/python3.10/site-packages/__pycache__/ghp_import.cpython-310.pyc new file mode 100644 index 000000000..6ba3da539 Binary files /dev/null and b/venv/lib/python3.10/site-packages/__pycache__/ghp_import.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/__pycache__/six.cpython-310.pyc b/venv/lib/python3.10/site-packages/__pycache__/six.cpython-310.pyc new file mode 100644 index 000000000..bea4bb746 Binary files /dev/null and b/venv/lib/python3.10/site-packages/__pycache__/six.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/__pycache__/yaml_env_tag.cpython-310.pyc b/venv/lib/python3.10/site-packages/__pycache__/yaml_env_tag.cpython-310.pyc new file mode 100644 index 000000000..61297f0f8 Binary files /dev/null and b/venv/lib/python3.10/site-packages/__pycache__/yaml_env_tag.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/_distutils_hack/__init__.py b/venv/lib/python3.10/site-packages/_distutils_hack/__init__.py new file mode 100644 index 000000000..f987a5367 --- /dev/null +++ b/venv/lib/python3.10/site-packages/_distutils_hack/__init__.py @@ -0,0 +1,222 @@ +# don't import any costly modules +import sys +import os + + +is_pypy = '__pypy__' in sys.builtin_module_names + + +def warn_distutils_present(): + if 'distutils' not in sys.modules: + return + if is_pypy and sys.version_info < (3, 7): + # PyPy for 3.6 unconditionally imports distutils, so bypass the warning + # https://foss.heptapod.net/pypy/pypy/-/blob/be829135bc0d758997b3566062999ee8b23872b4/lib-python/3/site.py#L250 + return + import warnings + + warnings.warn( + "Distutils was imported before Setuptools, but importing Setuptools " + "also replaces the `distutils` module in `sys.modules`. This may lead " + "to undesirable behaviors or errors. To avoid these issues, avoid " + "using distutils directly, ensure that setuptools is installed in the " + "traditional way (e.g. not an editable install), and/or make sure " + "that setuptools is always imported before distutils." + ) + + +def clear_distutils(): + if 'distutils' not in sys.modules: + return + import warnings + + warnings.warn("Setuptools is replacing distutils.") + mods = [ + name + for name in sys.modules + if name == "distutils" or name.startswith("distutils.") + ] + for name in mods: + del sys.modules[name] + + +def enabled(): + """ + Allow selection of distutils by environment variable. + """ + which = os.environ.get('SETUPTOOLS_USE_DISTUTILS', 'local') + return which == 'local' + + +def ensure_local_distutils(): + import importlib + + clear_distutils() + + # With the DistutilsMetaFinder in place, + # perform an import to cause distutils to be + # loaded from setuptools._distutils. Ref #2906. + with shim(): + importlib.import_module('distutils') + + # check that submodules load as expected + core = importlib.import_module('distutils.core') + assert '_distutils' in core.__file__, core.__file__ + assert 'setuptools._distutils.log' not in sys.modules + + +def do_override(): + """ + Ensure that the local copy of distutils is preferred over stdlib. + + See https://github.com/pypa/setuptools/issues/417#issuecomment-392298401 + for more motivation. + """ + if enabled(): + warn_distutils_present() + ensure_local_distutils() + + +class _TrivialRe: + def __init__(self, *patterns): + self._patterns = patterns + + def match(self, string): + return all(pat in string for pat in self._patterns) + + +class DistutilsMetaFinder: + def find_spec(self, fullname, path, target=None): + # optimization: only consider top level modules and those + # found in the CPython test suite. + if path is not None and not fullname.startswith('test.'): + return + + method_name = 'spec_for_{fullname}'.format(**locals()) + method = getattr(self, method_name, lambda: None) + return method() + + def spec_for_distutils(self): + if self.is_cpython(): + return + + import importlib + import importlib.abc + import importlib.util + + try: + mod = importlib.import_module('setuptools._distutils') + except Exception: + # There are a couple of cases where setuptools._distutils + # may not be present: + # - An older Setuptools without a local distutils is + # taking precedence. Ref #2957. + # - Path manipulation during sitecustomize removes + # setuptools from the path but only after the hook + # has been loaded. Ref #2980. + # In either case, fall back to stdlib behavior. + return + + class DistutilsLoader(importlib.abc.Loader): + def create_module(self, spec): + mod.__name__ = 'distutils' + return mod + + def exec_module(self, module): + pass + + return importlib.util.spec_from_loader( + 'distutils', DistutilsLoader(), origin=mod.__file__ + ) + + @staticmethod + def is_cpython(): + """ + Suppress supplying distutils for CPython (build and tests). + Ref #2965 and #3007. + """ + return os.path.isfile('pybuilddir.txt') + + def spec_for_pip(self): + """ + Ensure stdlib distutils when running under pip. + See pypa/pip#8761 for rationale. + """ + if self.pip_imported_during_build(): + return + clear_distutils() + self.spec_for_distutils = lambda: None + + @classmethod + def pip_imported_during_build(cls): + """ + Detect if pip is being imported in a build script. Ref #2355. + """ + import traceback + + return any( + cls.frame_file_is_setup(frame) for frame, line in traceback.walk_stack(None) + ) + + @staticmethod + def frame_file_is_setup(frame): + """ + Return True if the indicated frame suggests a setup.py file. + """ + # some frames may not have __file__ (#2940) + return frame.f_globals.get('__file__', '').endswith('setup.py') + + def spec_for_sensitive_tests(self): + """ + Ensure stdlib distutils when running select tests under CPython. + + python/cpython#91169 + """ + clear_distutils() + self.spec_for_distutils = lambda: None + + sensitive_tests = ( + [ + 'test.test_distutils', + 'test.test_peg_generator', + 'test.test_importlib', + ] + if sys.version_info < (3, 10) + else [ + 'test.test_distutils', + ] + ) + + +for name in DistutilsMetaFinder.sensitive_tests: + setattr( + DistutilsMetaFinder, + f'spec_for_{name}', + DistutilsMetaFinder.spec_for_sensitive_tests, + ) + + +DISTUTILS_FINDER = DistutilsMetaFinder() + + +def add_shim(): + DISTUTILS_FINDER in sys.meta_path or insert_shim() + + +class shim: + def __enter__(self): + insert_shim() + + def __exit__(self, exc, value, tb): + remove_shim() + + +def insert_shim(): + sys.meta_path.insert(0, DISTUTILS_FINDER) + + +def remove_shim(): + try: + sys.meta_path.remove(DISTUTILS_FINDER) + except ValueError: + pass diff --git a/venv/lib/python3.10/site-packages/_distutils_hack/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/_distutils_hack/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 000000000..62cb83570 Binary files /dev/null and b/venv/lib/python3.10/site-packages/_distutils_hack/__pycache__/__init__.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/_distutils_hack/__pycache__/override.cpython-310.pyc b/venv/lib/python3.10/site-packages/_distutils_hack/__pycache__/override.cpython-310.pyc new file mode 100644 index 000000000..66bd3b350 Binary files /dev/null and b/venv/lib/python3.10/site-packages/_distutils_hack/__pycache__/override.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/_distutils_hack/override.py b/venv/lib/python3.10/site-packages/_distutils_hack/override.py new file mode 100644 index 000000000..2cc433a4a --- /dev/null +++ b/venv/lib/python3.10/site-packages/_distutils_hack/override.py @@ -0,0 +1 @@ +__import__('_distutils_hack').do_override() diff --git a/venv/lib/python3.10/site-packages/_watchdog_fsevents.cpython-310-darwin.so b/venv/lib/python3.10/site-packages/_watchdog_fsevents.cpython-310-darwin.so new file mode 100755 index 000000000..25c1caed8 Binary files /dev/null and b/venv/lib/python3.10/site-packages/_watchdog_fsevents.cpython-310-darwin.so differ diff --git a/venv/lib/python3.10/site-packages/_yaml/__init__.py b/venv/lib/python3.10/site-packages/_yaml/__init__.py new file mode 100644 index 000000000..7baa8c4b6 --- /dev/null +++ b/venv/lib/python3.10/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.10/site-packages/_yaml/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/_yaml/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 000000000..8ffc3b784 Binary files /dev/null and b/venv/lib/python3.10/site-packages/_yaml/__pycache__/__init__.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/babel/__init__.py b/venv/lib/python3.10/site-packages/babel/__init__.py new file mode 100644 index 000000000..e4aca9347 --- /dev/null +++ b/venv/lib/python3.10/site-packages/babel/__init__.py @@ -0,0 +1,37 @@ +""" + 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-2023 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.12.1' + +__all__ = [ + 'Locale', + 'UnknownLocaleError', + 'default_locale', + 'get_locale_identifier', + 'negotiate_locale', + 'parse_locale', +] diff --git a/venv/lib/python3.10/site-packages/babel/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/babel/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 000000000..65fde39e0 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/__pycache__/__init__.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/babel/__pycache__/core.cpython-310.pyc b/venv/lib/python3.10/site-packages/babel/__pycache__/core.cpython-310.pyc new file mode 100644 index 000000000..b10411064 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/__pycache__/core.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/babel/__pycache__/dates.cpython-310.pyc b/venv/lib/python3.10/site-packages/babel/__pycache__/dates.cpython-310.pyc new file mode 100644 index 000000000..136278280 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/__pycache__/dates.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/babel/__pycache__/languages.cpython-310.pyc b/venv/lib/python3.10/site-packages/babel/__pycache__/languages.cpython-310.pyc new file mode 100644 index 000000000..7b4d8ba3b Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/__pycache__/languages.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/babel/__pycache__/lists.cpython-310.pyc b/venv/lib/python3.10/site-packages/babel/__pycache__/lists.cpython-310.pyc new file mode 100644 index 000000000..5993949d6 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/__pycache__/lists.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/babel/__pycache__/localedata.cpython-310.pyc b/venv/lib/python3.10/site-packages/babel/__pycache__/localedata.cpython-310.pyc new file mode 100644 index 000000000..a79d891db Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/__pycache__/localedata.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/babel/__pycache__/numbers.cpython-310.pyc b/venv/lib/python3.10/site-packages/babel/__pycache__/numbers.cpython-310.pyc new file mode 100644 index 000000000..ecb919072 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/__pycache__/numbers.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/babel/__pycache__/plural.cpython-310.pyc b/venv/lib/python3.10/site-packages/babel/__pycache__/plural.cpython-310.pyc new file mode 100644 index 000000000..a0323c56b Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/__pycache__/plural.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/babel/__pycache__/support.cpython-310.pyc b/venv/lib/python3.10/site-packages/babel/__pycache__/support.cpython-310.pyc new file mode 100644 index 000000000..5367803bb Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/__pycache__/support.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/babel/__pycache__/units.cpython-310.pyc b/venv/lib/python3.10/site-packages/babel/__pycache__/units.cpython-310.pyc new file mode 100644 index 000000000..e7f048b29 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/__pycache__/units.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/babel/__pycache__/util.cpython-310.pyc b/venv/lib/python3.10/site-packages/babel/__pycache__/util.cpython-310.pyc new file mode 100644 index 000000000..48e653933 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/__pycache__/util.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/babel/core.py b/venv/lib/python3.10/site-packages/babel/core.py new file mode 100644 index 000000000..6df585083 --- /dev/null +++ b/venv/lib/python3.10/site-packages/babel/core.py @@ -0,0 +1,1258 @@ +""" + babel.core + ~~~~~~~~~~ + + Core locale representation and locale data access. + + :copyright: (c) 2013-2023 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 + +from babel import localedata +from babel.plural import PluralRule + +__all__ = ['UnknownLocaleError', 'Locale', 'default_locale', 'negotiate_locale', + 'parse_locale'] + +if TYPE_CHECKING: + from typing_extensions import Literal, TypeAlias + + _GLOBAL_KEY: TypeAlias = Literal[ + "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", + ] + + _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['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 = None + + identifier = str(self) + identifier_without_modifier = identifier.partition('@')[0] + if not localedata.exists(identifier_without_modifier): + 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 + """ + identifier = negotiate_locale(preferred, available, sep=sep, + aliases=aliases) + if identifier: + return Locale.parse(identifier, sep=sep) + + @classmethod + def parse( + cls, + identifier: str | Locale | 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') + + 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` + """ + if isinstance(identifier, Locale): + return identifier + elif 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(str(self))) + 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) + details = filter(None, details) + if details: + retval += f" ({', '.join(details)})" + 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. + + .. note:: The format of the value returned may change between + Babel versions. + + >>> Locale('fr', 'FR').number_symbols['decimal'] + u',' + """ + return self._data['number_symbols'] + + @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. \u2013 ', 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 | 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 of the ``LC_XXX`` environment variable names + :param aliases: a dictionary of aliases for locale identifiers + """ + varnames = (category, 'LANGUAGE', 'LC_ALL', 'LC_CTYPE', 'LANG') + for name in filter(None, varnames): + 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 + + +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 + """ + 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.10/site-packages/babel/dates.py b/venv/lib/python3.10/site-packages/babel/dates.py new file mode 100644 index 000000000..eb1019e89 --- /dev/null +++ b/venv/lib/python3.10/site-packages/babel/dates.py @@ -0,0 +1,1911 @@ +""" + 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-2023 by the Babel Team. + :license: BSD, see LICENSE for more details. +""" + +from __future__ import annotations + +import re +import warnings +from functools import lru_cache +from typing import TYPE_CHECKING, 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 Literal, 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.utcnow()) + + +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.utcnow() + elif isinstance(instant, (int, float)): + return datetime.datetime.utcfromtimestamp(instant) + 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.utcnow() + elif isinstance(time, (int, float)): + time = datetime.datetime.utcfromtimestamp(time) + + 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 = LC_TIME) -> 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 + """ + return Locale.parse(locale).day_periods[context][width] + + +def get_day_names(width: Literal['abbreviated', 'narrow', 'short', 'wide'] = 'wide', + context: _Context = 'format', locale: Locale | str | None = LC_TIME) -> 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 + """ + return Locale.parse(locale).days[context][width] + + +def get_month_names(width: Literal['abbreviated', 'narrow', 'wide'] = 'wide', + context: _Context = 'format', locale: Locale | str | None = LC_TIME) -> 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 + """ + return Locale.parse(locale).months[context][width] + + +def get_quarter_names(width: Literal['abbreviated', 'narrow', 'wide'] = 'wide', + context: _Context = 'format', locale: Locale | str | None = LC_TIME) -> 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 + """ + return Locale.parse(locale).quarters[context][width] + + +def get_era_names(width: Literal['abbreviated', 'narrow', 'wide'] = 'wide', + locale: Locale | str | None = LC_TIME) -> 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 + """ + return Locale.parse(locale).eras[width] + + +def get_date_format(format: _PredefinedTimeFormat = 'medium', locale: Locale | str | None = LC_TIME) -> 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 + """ + return Locale.parse(locale).date_formats[format] + + +def get_datetime_format(format: _PredefinedTimeFormat = 'medium', locale: Locale | str | None = LC_TIME) -> 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 + """ + patterns = Locale.parse(locale).datetime_formats + if format not in patterns: + format = None + return patterns[format] + + +def get_time_format(format: _PredefinedTimeFormat = 'medium', locale: Locale | str | None = LC_TIME) -> 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 + """ + return Locale.parse(locale).time_formats[format] + + +def get_timezone_gmt( + datetime: _Instant = None, + width: Literal['long', 'short', 'iso8601', 'iso8601_short'] = 'long', + locale: Locale | str | None = LC_TIME, + 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 + :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) + + 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 = LC_TIME, + 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 + :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) + + 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 = LC_TIME, + 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') + 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') + 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 + :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) + + 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 = LC_TIME, +) -> 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 + """ + if date is None: + date = datetime.date.today() + elif isinstance(date, datetime.datetime): + date = date.date() + + locale = Locale.parse(locale) + 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 = LC_TIME, +) -> 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 + """ + datetime = _ensure_datetime_tzinfo(_get_datetime(datetime), tzinfo) + + locale = Locale.parse(locale) + 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 = LC_TIME, +) -> 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') + u'15:30:00 heure normale d\u2019Europe centrale' + >>> format_time(t, format='full', tzinfo=get_timezone('US/Eastern'), + ... locale='en_US') + 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 + """ + + # 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) + 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 = LC_TIME, +) -> 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 + + 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. + :param locale: a `Locale` object or a locale identifier + """ + locale = Locale.parse(locale) + 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 = LC_TIME, +) -> 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 + """ + 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) + format = 'long' + if isinstance(delta, datetime.timedelta): + seconds = int((delta.days * 86400) + delta.seconds) + else: + seconds = delta + locale = Locale.parse(locale) + + def _iter_patterns(a_unit): + if add_direction: + unit_rel_patterns = locale._data['date_fields'][a_unit] + if seconds >= 0: + yield unit_rel_patterns['future'] + else: + yield unit_rel_patterns['past'] + a_unit = f"duration-{a_unit}" + yield locale._data['unit_patterns'].get(a_unit, {}).get(format) + + 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') + 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 | None = LC_TIME, +) -> 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 = LC_TIME, +) -> 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. + :return: Formatted interval + """ + locale = Locale.parse(locale) + + # 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 = LC_TIME, +) -> 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 + :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) + + # 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 = LC_TIME, + format: _PredefinedTimeFormat = 'medium', +) -> datetime.date: + """Parse a date from a string. + + This function first tries to interpret the string as ISO-8601 + date format, 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) + + :param string: the string containing the date + :param locale: a `Locale` object or a locale identifier + :param format: the format to use (see ``get_date_format``) + """ + numbers = re.findall(r'(\d+)', string) + if not numbers: + raise ParseError("No numbers were found in input") + + # 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: + try: + return datetime.date(*map(int, iso_alike.groups())) + except ValueError: + pass # a locale format might fit better, so let's continue + + format_str = get_date_format(format=format, locale=locale).pattern.lower() + year_idx = format_str.index('y') + month_idx = format_str.index('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 = LC_TIME, + format: _PredefinedTimeFormat = '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. + + >>> parse_time('15:30:00', locale='en_US') + datetime.time(15, 30) + + :param string: the string containing the time + :param locale: a `Locale` object or a locale identifier + :param format: the format to use (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? + format_str = get_time_format(format=format, locale=locale).pattern.lower() + hour_idx = format_str.index('h') + if hour_idx < 0: + hour_idx = format_str.index('k') + min_idx = format_str.index('m') + sec_idx = format_str.index('s') + + 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(): + value = self.value.isocalendar()[0] + 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 + day_of_year = self.get_day_of_year() + week = self.get_week_number(day_of_year) + if week == 0: + date = self.value - datetime.timedelta(days=day_of_year) + week = self.get_week_number(self.get_day_of_year(date), + date.weekday()) + return self.format(week, num) + else: # week of month + week = self.get_week_number(self.value.day) + if week == 0: + date = self.value - datetime.timedelta(days=self.value.day) + week = self.get_week_number(date.day, date.weekday()) + 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'清晨' + >>> 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_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 + + if self.locale.first_week_day == 0: + # Correct the weeknumber in case of iso-calendar usage (first_week_day=0). + # If the weeknumber exceeds the maximum number of weeks for the given year + # we must count from zero.For example the above calculation gives week 53 + # for 2018-12-31. By iso-calender definition 2018 has a max of 52 + # weeks, thus the weeknumber must be 53-52=1. + max_weeks = datetime.date(year=self.value.year, day=28, month=12).isocalendar()[1] + if week_number > max_weeks: + week_number -= max_weeks + + 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] + :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 + # http://source.icu-project.org/repos/icu/icu4j/trunk/main/classes/core/src/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') + + 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.10/site-packages/babel/global.dat b/venv/lib/python3.10/site-packages/babel/global.dat new file mode 100644 index 000000000..5b74cfcb4 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/global.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/languages.py b/venv/lib/python3.10/site-packages/babel/languages.py new file mode 100644 index 000000000..564f555d2 --- /dev/null +++ b/venv/lib/python3.10/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.10/site-packages/babel/lists.py b/venv/lib/python3.10/site-packages/babel/lists.py new file mode 100644 index 000000000..6ea4f014a --- /dev/null +++ b/venv/lib/python3.10/site-packages/babel/lists.py @@ -0,0 +1,94 @@ +""" + 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-2023 by the Babel Team. + :license: BSD, see LICENSE for more details. +""" +from __future__ import annotations + +from collections.abc import Sequence +from typing import TYPE_CHECKING + +from babel.core import Locale, default_locale + +if TYPE_CHECKING: + from typing_extensions import Literal + +DEFAULT_LOCALE = default_locale() + + +def format_list(lst: Sequence[str], + style: Literal['standard', 'standard-short', 'or', 'or-short', 'unit', 'unit-short', 'unit-narrow'] = 'standard', + locale: Locale | str | None = DEFAULT_LOCALE) -> 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' + + These styles are defined, but not all are necessarily available in all locales. + 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 a '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 + """ + locale = Locale.parse(locale) + if not lst: + return '' + if len(lst) == 1: + return lst[0] + + if style not in locale.list_patterns: + raise ValueError( + f'Locale {locale} does not support list formatting style {style!r} ' + f'(supported are {sorted(locale.list_patterns)})' + ) + patterns = locale.list_patterns[style] + + if len(lst) == 2: + 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 diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/af.dat b/venv/lib/python3.10/site-packages/babel/locale-data/af.dat new file mode 100644 index 000000000..3695a7a8e Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/af.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/af_NA.dat b/venv/lib/python3.10/site-packages/babel/locale-data/af_NA.dat new file mode 100644 index 000000000..a71c4e7af Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/af_NA.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/af_ZA.dat b/venv/lib/python3.10/site-packages/babel/locale-data/af_ZA.dat new file mode 100644 index 000000000..b7487d7f5 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/af_ZA.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/agq.dat b/venv/lib/python3.10/site-packages/babel/locale-data/agq.dat new file mode 100644 index 000000000..3b7e5fa2f Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/agq.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/agq_CM.dat b/venv/lib/python3.10/site-packages/babel/locale-data/agq_CM.dat new file mode 100644 index 000000000..4c968efc2 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/agq_CM.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ak.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ak.dat new file mode 100644 index 000000000..24a6be5cc Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ak.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ak_GH.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ak_GH.dat new file mode 100644 index 000000000..c2b445542 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ak_GH.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/am.dat b/venv/lib/python3.10/site-packages/babel/locale-data/am.dat new file mode 100644 index 000000000..f64dd66ca Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/am.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/am_ET.dat b/venv/lib/python3.10/site-packages/babel/locale-data/am_ET.dat new file mode 100644 index 000000000..2fc6bd41f Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/am_ET.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ann.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ann.dat new file mode 100644 index 000000000..d265b2f0a Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ann.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ann_NG.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ann_NG.dat new file mode 100644 index 000000000..710caa437 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ann_NG.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ar.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ar.dat new file mode 100644 index 000000000..b1eded086 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ar.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ar_001.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ar_001.dat new file mode 100644 index 000000000..b459a964e Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ar_001.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ar_AE.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ar_AE.dat new file mode 100644 index 000000000..b71b9f7a5 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ar_AE.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ar_BH.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ar_BH.dat new file mode 100644 index 000000000..d5208f012 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ar_BH.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ar_DJ.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ar_DJ.dat new file mode 100644 index 000000000..e26820019 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ar_DJ.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ar_DZ.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ar_DZ.dat new file mode 100644 index 000000000..6f28eb798 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ar_DZ.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ar_EG.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ar_EG.dat new file mode 100644 index 000000000..c3b60c837 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ar_EG.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ar_EH.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ar_EH.dat new file mode 100644 index 000000000..73bec132b Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ar_EH.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ar_ER.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ar_ER.dat new file mode 100644 index 000000000..03e0803a3 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ar_ER.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ar_IL.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ar_IL.dat new file mode 100644 index 000000000..f4b3466a4 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ar_IL.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ar_IQ.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ar_IQ.dat new file mode 100644 index 000000000..f44af3a88 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ar_IQ.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ar_JO.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ar_JO.dat new file mode 100644 index 000000000..4fc364cfa Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ar_JO.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ar_KM.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ar_KM.dat new file mode 100644 index 000000000..9d40c4950 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ar_KM.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ar_KW.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ar_KW.dat new file mode 100644 index 000000000..c75957c41 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ar_KW.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ar_LB.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ar_LB.dat new file mode 100644 index 000000000..6877e4c97 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ar_LB.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ar_LY.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ar_LY.dat new file mode 100644 index 000000000..c36de79e0 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ar_LY.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ar_MA.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ar_MA.dat new file mode 100644 index 000000000..e4d646665 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ar_MA.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ar_MR.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ar_MR.dat new file mode 100644 index 000000000..a79d11782 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ar_MR.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ar_OM.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ar_OM.dat new file mode 100644 index 000000000..2e9eb8006 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ar_OM.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ar_PS.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ar_PS.dat new file mode 100644 index 000000000..322f0f170 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ar_PS.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ar_QA.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ar_QA.dat new file mode 100644 index 000000000..609074c2b Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ar_QA.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ar_SA.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ar_SA.dat new file mode 100644 index 000000000..92dc0c903 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ar_SA.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ar_SD.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ar_SD.dat new file mode 100644 index 000000000..dab641511 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ar_SD.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ar_SO.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ar_SO.dat new file mode 100644 index 000000000..e2d462e63 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ar_SO.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ar_SS.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ar_SS.dat new file mode 100644 index 000000000..d4d7bdfcc Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ar_SS.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ar_SY.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ar_SY.dat new file mode 100644 index 000000000..018cccbf7 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ar_SY.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ar_TD.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ar_TD.dat new file mode 100644 index 000000000..3ee8f73dc Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ar_TD.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ar_TN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ar_TN.dat new file mode 100644 index 000000000..1b4d7b578 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ar_TN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ar_YE.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ar_YE.dat new file mode 100644 index 000000000..e135658ba Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ar_YE.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/as.dat b/venv/lib/python3.10/site-packages/babel/locale-data/as.dat new file mode 100644 index 000000000..83f9de4ce Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/as.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/as_IN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/as_IN.dat new file mode 100644 index 000000000..647a92ac5 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/as_IN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/asa.dat b/venv/lib/python3.10/site-packages/babel/locale-data/asa.dat new file mode 100644 index 000000000..68f97b9fb Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/asa.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/asa_TZ.dat b/venv/lib/python3.10/site-packages/babel/locale-data/asa_TZ.dat new file mode 100644 index 000000000..190a6dd3e Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/asa_TZ.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ast.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ast.dat new file mode 100644 index 000000000..5cb6ca8a9 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ast.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ast_ES.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ast_ES.dat new file mode 100644 index 000000000..3a30d747a Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ast_ES.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/az.dat b/venv/lib/python3.10/site-packages/babel/locale-data/az.dat new file mode 100644 index 000000000..c5cf314fa Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/az.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/az_Cyrl.dat b/venv/lib/python3.10/site-packages/babel/locale-data/az_Cyrl.dat new file mode 100644 index 000000000..877eae950 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/az_Cyrl.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/az_Cyrl_AZ.dat b/venv/lib/python3.10/site-packages/babel/locale-data/az_Cyrl_AZ.dat new file mode 100644 index 000000000..8fc93a30b Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/az_Cyrl_AZ.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/az_Latn.dat b/venv/lib/python3.10/site-packages/babel/locale-data/az_Latn.dat new file mode 100644 index 000000000..f2b58c72f Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/az_Latn.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/az_Latn_AZ.dat b/venv/lib/python3.10/site-packages/babel/locale-data/az_Latn_AZ.dat new file mode 100644 index 000000000..8fc93a30b Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/az_Latn_AZ.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/bas.dat b/venv/lib/python3.10/site-packages/babel/locale-data/bas.dat new file mode 100644 index 000000000..c9f33c170 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/bas.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/bas_CM.dat b/venv/lib/python3.10/site-packages/babel/locale-data/bas_CM.dat new file mode 100644 index 000000000..2936508f1 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/bas_CM.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/be.dat b/venv/lib/python3.10/site-packages/babel/locale-data/be.dat new file mode 100644 index 000000000..c00b99966 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/be.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/be_BY.dat b/venv/lib/python3.10/site-packages/babel/locale-data/be_BY.dat new file mode 100644 index 000000000..3a9f98917 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/be_BY.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/be_TARASK.dat b/venv/lib/python3.10/site-packages/babel/locale-data/be_TARASK.dat new file mode 100644 index 000000000..0007b965f Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/be_TARASK.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/bem.dat b/venv/lib/python3.10/site-packages/babel/locale-data/bem.dat new file mode 100644 index 000000000..990de0627 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/bem.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/bem_ZM.dat b/venv/lib/python3.10/site-packages/babel/locale-data/bem_ZM.dat new file mode 100644 index 000000000..26b669725 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/bem_ZM.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/bez.dat b/venv/lib/python3.10/site-packages/babel/locale-data/bez.dat new file mode 100644 index 000000000..34510b998 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/bez.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/bez_TZ.dat b/venv/lib/python3.10/site-packages/babel/locale-data/bez_TZ.dat new file mode 100644 index 000000000..dc04ff732 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/bez_TZ.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/bg.dat b/venv/lib/python3.10/site-packages/babel/locale-data/bg.dat new file mode 100644 index 000000000..cf703c8a8 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/bg.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/bg_BG.dat b/venv/lib/python3.10/site-packages/babel/locale-data/bg_BG.dat new file mode 100644 index 000000000..0ccd49215 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/bg_BG.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/bgc.dat b/venv/lib/python3.10/site-packages/babel/locale-data/bgc.dat new file mode 100644 index 000000000..1363e7f44 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/bgc.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/bgc_IN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/bgc_IN.dat new file mode 100644 index 000000000..8b54ead20 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/bgc_IN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/bho.dat b/venv/lib/python3.10/site-packages/babel/locale-data/bho.dat new file mode 100644 index 000000000..f566a3b76 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/bho.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/bho_IN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/bho_IN.dat new file mode 100644 index 000000000..3d2eace99 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/bho_IN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/bm.dat b/venv/lib/python3.10/site-packages/babel/locale-data/bm.dat new file mode 100644 index 000000000..2a01edb7c Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/bm.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/bm_ML.dat b/venv/lib/python3.10/site-packages/babel/locale-data/bm_ML.dat new file mode 100644 index 000000000..892c2649b Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/bm_ML.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/bn.dat b/venv/lib/python3.10/site-packages/babel/locale-data/bn.dat new file mode 100644 index 000000000..ce644785c Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/bn.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/bn_BD.dat b/venv/lib/python3.10/site-packages/babel/locale-data/bn_BD.dat new file mode 100644 index 000000000..0d3834e24 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/bn_BD.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/bn_IN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/bn_IN.dat new file mode 100644 index 000000000..75fdab8be Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/bn_IN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/bo.dat b/venv/lib/python3.10/site-packages/babel/locale-data/bo.dat new file mode 100644 index 000000000..abff63468 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/bo.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/bo_CN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/bo_CN.dat new file mode 100644 index 000000000..e43b531aa Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/bo_CN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/bo_IN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/bo_IN.dat new file mode 100644 index 000000000..1585df4b2 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/bo_IN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/br.dat b/venv/lib/python3.10/site-packages/babel/locale-data/br.dat new file mode 100644 index 000000000..c2ba09926 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/br.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/br_FR.dat b/venv/lib/python3.10/site-packages/babel/locale-data/br_FR.dat new file mode 100644 index 000000000..bdaeb3bd1 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/br_FR.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/brx.dat b/venv/lib/python3.10/site-packages/babel/locale-data/brx.dat new file mode 100644 index 000000000..731ace981 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/brx.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/brx_IN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/brx_IN.dat new file mode 100644 index 000000000..f3054ec88 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/brx_IN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/bs.dat b/venv/lib/python3.10/site-packages/babel/locale-data/bs.dat new file mode 100644 index 000000000..2b802bf8b Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/bs.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/bs_Cyrl.dat b/venv/lib/python3.10/site-packages/babel/locale-data/bs_Cyrl.dat new file mode 100644 index 000000000..8b9992476 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/bs_Cyrl.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/bs_Cyrl_BA.dat b/venv/lib/python3.10/site-packages/babel/locale-data/bs_Cyrl_BA.dat new file mode 100644 index 000000000..66476e8fb Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/bs_Cyrl_BA.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/bs_Latn.dat b/venv/lib/python3.10/site-packages/babel/locale-data/bs_Latn.dat new file mode 100644 index 000000000..ff389ae37 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/bs_Latn.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/bs_Latn_BA.dat b/venv/lib/python3.10/site-packages/babel/locale-data/bs_Latn_BA.dat new file mode 100644 index 000000000..66476e8fb Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/bs_Latn_BA.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ca.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ca.dat new file mode 100644 index 000000000..aa48a98e5 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ca.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ca_AD.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ca_AD.dat new file mode 100644 index 000000000..af3db59e3 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ca_AD.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ca_ES.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ca_ES.dat new file mode 100644 index 000000000..64798c0e5 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ca_ES.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ca_ES_VALENCIA.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ca_ES_VALENCIA.dat new file mode 100644 index 000000000..58e458850 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ca_ES_VALENCIA.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ca_FR.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ca_FR.dat new file mode 100644 index 000000000..f734323ed Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ca_FR.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ca_IT.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ca_IT.dat new file mode 100644 index 000000000..5802b4fdf Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ca_IT.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ccp.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ccp.dat new file mode 100644 index 000000000..a03c8814a Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ccp.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ccp_BD.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ccp_BD.dat new file mode 100644 index 000000000..bb1e6257f Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ccp_BD.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ccp_IN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ccp_IN.dat new file mode 100644 index 000000000..78f882942 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ccp_IN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ce.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ce.dat new file mode 100644 index 000000000..8b2a25340 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ce.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ce_RU.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ce_RU.dat new file mode 100644 index 000000000..4968d9335 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ce_RU.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ceb.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ceb.dat new file mode 100644 index 000000000..18c2fcd95 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ceb.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ceb_PH.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ceb_PH.dat new file mode 100644 index 000000000..4f99a2356 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ceb_PH.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/cgg.dat b/venv/lib/python3.10/site-packages/babel/locale-data/cgg.dat new file mode 100644 index 000000000..3a98eb59e Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/cgg.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/cgg_UG.dat b/venv/lib/python3.10/site-packages/babel/locale-data/cgg_UG.dat new file mode 100644 index 000000000..252541488 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/cgg_UG.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/chr.dat b/venv/lib/python3.10/site-packages/babel/locale-data/chr.dat new file mode 100644 index 000000000..0114b275c Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/chr.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/chr_US.dat b/venv/lib/python3.10/site-packages/babel/locale-data/chr_US.dat new file mode 100644 index 000000000..96e967fbe Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/chr_US.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ckb.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ckb.dat new file mode 100644 index 000000000..fbbd96acd Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ckb.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ckb_IQ.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ckb_IQ.dat new file mode 100644 index 000000000..e7c108e1c Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ckb_IQ.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ckb_IR.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ckb_IR.dat new file mode 100644 index 000000000..fc4f2c0b2 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ckb_IR.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/cs.dat b/venv/lib/python3.10/site-packages/babel/locale-data/cs.dat new file mode 100644 index 000000000..fd37541b6 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/cs.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/cs_CZ.dat b/venv/lib/python3.10/site-packages/babel/locale-data/cs_CZ.dat new file mode 100644 index 000000000..04b0b1a32 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/cs_CZ.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/cv.dat b/venv/lib/python3.10/site-packages/babel/locale-data/cv.dat new file mode 100644 index 000000000..05b8384d8 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/cv.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/cv_RU.dat b/venv/lib/python3.10/site-packages/babel/locale-data/cv_RU.dat new file mode 100644 index 000000000..6c09e43f6 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/cv_RU.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/cy.dat b/venv/lib/python3.10/site-packages/babel/locale-data/cy.dat new file mode 100644 index 000000000..cb74093a0 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/cy.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/cy_GB.dat b/venv/lib/python3.10/site-packages/babel/locale-data/cy_GB.dat new file mode 100644 index 000000000..1f75973f0 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/cy_GB.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/da.dat b/venv/lib/python3.10/site-packages/babel/locale-data/da.dat new file mode 100644 index 000000000..7e10ae29e Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/da.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/da_DK.dat b/venv/lib/python3.10/site-packages/babel/locale-data/da_DK.dat new file mode 100644 index 000000000..e24fd73b1 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/da_DK.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/da_GL.dat b/venv/lib/python3.10/site-packages/babel/locale-data/da_GL.dat new file mode 100644 index 000000000..1ee04208d Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/da_GL.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/dav.dat b/venv/lib/python3.10/site-packages/babel/locale-data/dav.dat new file mode 100644 index 000000000..6550904ee Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/dav.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/dav_KE.dat b/venv/lib/python3.10/site-packages/babel/locale-data/dav_KE.dat new file mode 100644 index 000000000..3f99b945e Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/dav_KE.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/de.dat b/venv/lib/python3.10/site-packages/babel/locale-data/de.dat new file mode 100644 index 000000000..2b73f94c7 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/de.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/de_AT.dat b/venv/lib/python3.10/site-packages/babel/locale-data/de_AT.dat new file mode 100644 index 000000000..8282fd638 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/de_AT.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/de_BE.dat b/venv/lib/python3.10/site-packages/babel/locale-data/de_BE.dat new file mode 100644 index 000000000..38d3e78ef Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/de_BE.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/de_CH.dat b/venv/lib/python3.10/site-packages/babel/locale-data/de_CH.dat new file mode 100644 index 000000000..720f1bfe9 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/de_CH.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/de_DE.dat b/venv/lib/python3.10/site-packages/babel/locale-data/de_DE.dat new file mode 100644 index 000000000..029c56698 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/de_DE.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/de_IT.dat b/venv/lib/python3.10/site-packages/babel/locale-data/de_IT.dat new file mode 100644 index 000000000..2bdc7f722 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/de_IT.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/de_LI.dat b/venv/lib/python3.10/site-packages/babel/locale-data/de_LI.dat new file mode 100644 index 000000000..e19e28a2e Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/de_LI.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/de_LU.dat b/venv/lib/python3.10/site-packages/babel/locale-data/de_LU.dat new file mode 100644 index 000000000..de686c4f8 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/de_LU.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/dje.dat b/venv/lib/python3.10/site-packages/babel/locale-data/dje.dat new file mode 100644 index 000000000..315a6f259 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/dje.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/dje_NE.dat b/venv/lib/python3.10/site-packages/babel/locale-data/dje_NE.dat new file mode 100644 index 000000000..bf016c4ba Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/dje_NE.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/doi.dat b/venv/lib/python3.10/site-packages/babel/locale-data/doi.dat new file mode 100644 index 000000000..ca8405f37 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/doi.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/doi_IN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/doi_IN.dat new file mode 100644 index 000000000..8f97f0e91 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/doi_IN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/dsb.dat b/venv/lib/python3.10/site-packages/babel/locale-data/dsb.dat new file mode 100644 index 000000000..cbfac9075 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/dsb.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/dsb_DE.dat b/venv/lib/python3.10/site-packages/babel/locale-data/dsb_DE.dat new file mode 100644 index 000000000..9bf80ab9f Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/dsb_DE.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/dua.dat b/venv/lib/python3.10/site-packages/babel/locale-data/dua.dat new file mode 100644 index 000000000..d6a56def9 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/dua.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/dua_CM.dat b/venv/lib/python3.10/site-packages/babel/locale-data/dua_CM.dat new file mode 100644 index 000000000..40011c6c8 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/dua_CM.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/dyo.dat b/venv/lib/python3.10/site-packages/babel/locale-data/dyo.dat new file mode 100644 index 000000000..2372b538e Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/dyo.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/dyo_SN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/dyo_SN.dat new file mode 100644 index 000000000..bb00f008e Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/dyo_SN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/dz.dat b/venv/lib/python3.10/site-packages/babel/locale-data/dz.dat new file mode 100644 index 000000000..f598d8493 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/dz.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/dz_BT.dat b/venv/lib/python3.10/site-packages/babel/locale-data/dz_BT.dat new file mode 100644 index 000000000..2bc440539 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/dz_BT.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ebu.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ebu.dat new file mode 100644 index 000000000..d75e139cd Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ebu.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ebu_KE.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ebu_KE.dat new file mode 100644 index 000000000..92a9c4f26 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ebu_KE.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ee.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ee.dat new file mode 100644 index 000000000..b74a7167d Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ee.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ee_GH.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ee_GH.dat new file mode 100644 index 000000000..46f075923 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ee_GH.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ee_TG.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ee_TG.dat new file mode 100644 index 000000000..a9211bef8 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ee_TG.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/el.dat b/venv/lib/python3.10/site-packages/babel/locale-data/el.dat new file mode 100644 index 000000000..0becceedb Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/el.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/el_CY.dat b/venv/lib/python3.10/site-packages/babel/locale-data/el_CY.dat new file mode 100644 index 000000000..22b376595 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/el_CY.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/el_GR.dat b/venv/lib/python3.10/site-packages/babel/locale-data/el_GR.dat new file mode 100644 index 000000000..49b1cbb42 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/el_GR.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en.dat new file mode 100644 index 000000000..42f4dc996 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_001.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_001.dat new file mode 100644 index 000000000..fcb4aaa57 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_001.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_150.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_150.dat new file mode 100644 index 000000000..2573c661d Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_150.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_AE.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_AE.dat new file mode 100644 index 000000000..22acb6e99 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_AE.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_AG.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_AG.dat new file mode 100644 index 000000000..846b084b4 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_AG.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_AI.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_AI.dat new file mode 100644 index 000000000..8e665ac1c Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_AI.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_AS.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_AS.dat new file mode 100644 index 000000000..27c1b87a5 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_AS.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_AT.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_AT.dat new file mode 100644 index 000000000..f73543dbc Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_AT.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_AU.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_AU.dat new file mode 100644 index 000000000..3fb65a9db Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_AU.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_BB.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_BB.dat new file mode 100644 index 000000000..7fad69867 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_BB.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_BE.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_BE.dat new file mode 100644 index 000000000..b31a6fa6b Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_BE.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_BI.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_BI.dat new file mode 100644 index 000000000..9763fa2df Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_BI.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_BM.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_BM.dat new file mode 100644 index 000000000..208321bcb Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_BM.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_BS.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_BS.dat new file mode 100644 index 000000000..02495295b Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_BS.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_BW.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_BW.dat new file mode 100644 index 000000000..febec1bc7 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_BW.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_BZ.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_BZ.dat new file mode 100644 index 000000000..ef410e562 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_BZ.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_CA.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_CA.dat new file mode 100644 index 000000000..648dc4159 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_CA.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_CC.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_CC.dat new file mode 100644 index 000000000..a32c7658d Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_CC.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_CH.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_CH.dat new file mode 100644 index 000000000..68c497885 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_CH.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_CK.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_CK.dat new file mode 100644 index 000000000..358fdb3ba Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_CK.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_CM.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_CM.dat new file mode 100644 index 000000000..17abaab52 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_CM.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_CX.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_CX.dat new file mode 100644 index 000000000..086f98fef Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_CX.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_CY.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_CY.dat new file mode 100644 index 000000000..69273f4a7 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_CY.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_DE.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_DE.dat new file mode 100644 index 000000000..ba2ccf439 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_DE.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_DG.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_DG.dat new file mode 100644 index 000000000..c80466eed Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_DG.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_DK.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_DK.dat new file mode 100644 index 000000000..ff10720a7 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_DK.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_DM.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_DM.dat new file mode 100644 index 000000000..46ea08274 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_DM.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_ER.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_ER.dat new file mode 100644 index 000000000..c1ac09045 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_ER.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_FI.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_FI.dat new file mode 100644 index 000000000..9fd8f8359 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_FI.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_FJ.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_FJ.dat new file mode 100644 index 000000000..b778afd75 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_FJ.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_FK.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_FK.dat new file mode 100644 index 000000000..8cb157d92 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_FK.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_FM.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_FM.dat new file mode 100644 index 000000000..e69fb2d3c Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_FM.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_GB.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_GB.dat new file mode 100644 index 000000000..352def4c2 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_GB.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_GD.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_GD.dat new file mode 100644 index 000000000..67bc7845c Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_GD.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_GG.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_GG.dat new file mode 100644 index 000000000..7161bccf3 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_GG.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_GH.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_GH.dat new file mode 100644 index 000000000..62744cbfc Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_GH.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_GI.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_GI.dat new file mode 100644 index 000000000..82a4b2fa1 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_GI.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_GM.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_GM.dat new file mode 100644 index 000000000..96f7e3ecb Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_GM.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_GU.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_GU.dat new file mode 100644 index 000000000..f909c165d Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_GU.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_GY.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_GY.dat new file mode 100644 index 000000000..5686ec05d Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_GY.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_HK.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_HK.dat new file mode 100644 index 000000000..a2b305137 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_HK.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_IE.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_IE.dat new file mode 100644 index 000000000..f9f1b0a28 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_IE.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_IL.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_IL.dat new file mode 100644 index 000000000..19d9abd5a Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_IL.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_IM.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_IM.dat new file mode 100644 index 000000000..f5f00ebd1 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_IM.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_IN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_IN.dat new file mode 100644 index 000000000..7c3ac278b Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_IN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_IO.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_IO.dat new file mode 100644 index 000000000..76ee975e4 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_IO.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_JE.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_JE.dat new file mode 100644 index 000000000..9fd958f5f Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_JE.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_JM.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_JM.dat new file mode 100644 index 000000000..5b4589871 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_JM.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_KE.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_KE.dat new file mode 100644 index 000000000..a37de240d Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_KE.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_KI.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_KI.dat new file mode 100644 index 000000000..3b189dc4f Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_KI.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_KN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_KN.dat new file mode 100644 index 000000000..978660789 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_KN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_KY.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_KY.dat new file mode 100644 index 000000000..77a51e68f Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_KY.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_LC.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_LC.dat new file mode 100644 index 000000000..bcc71d648 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_LC.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_LR.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_LR.dat new file mode 100644 index 000000000..896d3627f Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_LR.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_LS.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_LS.dat new file mode 100644 index 000000000..e8b1beb25 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_LS.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_MG.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_MG.dat new file mode 100644 index 000000000..1d1ae7102 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_MG.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_MH.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_MH.dat new file mode 100644 index 000000000..2e51eb3dd Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_MH.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_MO.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_MO.dat new file mode 100644 index 000000000..b4adfae51 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_MO.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_MP.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_MP.dat new file mode 100644 index 000000000..be799440d Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_MP.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_MS.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_MS.dat new file mode 100644 index 000000000..7b4706ed4 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_MS.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_MT.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_MT.dat new file mode 100644 index 000000000..38e4cabed Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_MT.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_MU.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_MU.dat new file mode 100644 index 000000000..842415b21 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_MU.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_MV.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_MV.dat new file mode 100644 index 000000000..ddd4e556b Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_MV.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_MW.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_MW.dat new file mode 100644 index 000000000..d6ba2d70b Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_MW.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_MY.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_MY.dat new file mode 100644 index 000000000..96baf032d Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_MY.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_NA.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_NA.dat new file mode 100644 index 000000000..4410af8a4 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_NA.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_NF.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_NF.dat new file mode 100644 index 000000000..808115043 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_NF.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_NG.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_NG.dat new file mode 100644 index 000000000..b863ef9a4 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_NG.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_NL.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_NL.dat new file mode 100644 index 000000000..7bd772202 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_NL.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_NR.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_NR.dat new file mode 100644 index 000000000..55f722875 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_NR.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_NU.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_NU.dat new file mode 100644 index 000000000..6ac54f584 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_NU.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_NZ.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_NZ.dat new file mode 100644 index 000000000..a88ef8553 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_NZ.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_PG.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_PG.dat new file mode 100644 index 000000000..2fb7788fc Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_PG.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_PH.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_PH.dat new file mode 100644 index 000000000..566adea45 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_PH.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_PK.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_PK.dat new file mode 100644 index 000000000..34d45d57d Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_PK.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_PN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_PN.dat new file mode 100644 index 000000000..d34279a06 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_PN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_PR.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_PR.dat new file mode 100644 index 000000000..154da6da2 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_PR.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_PW.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_PW.dat new file mode 100644 index 000000000..4c1dd0211 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_PW.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_RW.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_RW.dat new file mode 100644 index 000000000..f78cdeb22 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_RW.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_SB.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_SB.dat new file mode 100644 index 000000000..9a159df0a Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_SB.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_SC.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_SC.dat new file mode 100644 index 000000000..b4d8e7a42 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_SC.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_SD.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_SD.dat new file mode 100644 index 000000000..3073ee64f Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_SD.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_SE.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_SE.dat new file mode 100644 index 000000000..5ef60b9f8 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_SE.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_SG.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_SG.dat new file mode 100644 index 000000000..81df29fe4 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_SG.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_SH.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_SH.dat new file mode 100644 index 000000000..76d1c826c Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_SH.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_SI.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_SI.dat new file mode 100644 index 000000000..c919684c8 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_SI.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_SL.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_SL.dat new file mode 100644 index 000000000..2379cb6df Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_SL.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_SS.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_SS.dat new file mode 100644 index 000000000..413df19c2 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_SS.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_SX.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_SX.dat new file mode 100644 index 000000000..7b5e76eb2 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_SX.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_SZ.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_SZ.dat new file mode 100644 index 000000000..bddb16873 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_SZ.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_TC.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_TC.dat new file mode 100644 index 000000000..8f4f2793b Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_TC.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_TK.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_TK.dat new file mode 100644 index 000000000..918b92ba5 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_TK.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_TO.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_TO.dat new file mode 100644 index 000000000..4ee8f0676 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_TO.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_TT.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_TT.dat new file mode 100644 index 000000000..20a052def Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_TT.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_TV.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_TV.dat new file mode 100644 index 000000000..525ea2050 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_TV.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_TZ.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_TZ.dat new file mode 100644 index 000000000..110c9b744 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_TZ.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_UG.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_UG.dat new file mode 100644 index 000000000..c512424c3 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_UG.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_UM.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_UM.dat new file mode 100644 index 000000000..ccba323fe Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_UM.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_US.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_US.dat new file mode 100644 index 000000000..69c3299ea Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_US.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_US_POSIX.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_US_POSIX.dat new file mode 100644 index 000000000..0658bb472 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_US_POSIX.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_VC.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_VC.dat new file mode 100644 index 000000000..55bc09633 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_VC.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_VG.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_VG.dat new file mode 100644 index 000000000..0ff40d875 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_VG.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_VI.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_VI.dat new file mode 100644 index 000000000..09634abf5 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_VI.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_VU.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_VU.dat new file mode 100644 index 000000000..0236313a8 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_VU.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_WS.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_WS.dat new file mode 100644 index 000000000..898cf42bb Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_WS.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_ZA.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_ZA.dat new file mode 100644 index 000000000..2b4ff8d6c Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_ZA.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_ZM.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_ZM.dat new file mode 100644 index 000000000..3c6554464 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_ZM.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/en_ZW.dat b/venv/lib/python3.10/site-packages/babel/locale-data/en_ZW.dat new file mode 100644 index 000000000..d2711597f Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/en_ZW.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/eo.dat b/venv/lib/python3.10/site-packages/babel/locale-data/eo.dat new file mode 100644 index 000000000..e640ffc56 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/eo.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/eo_001.dat b/venv/lib/python3.10/site-packages/babel/locale-data/eo_001.dat new file mode 100644 index 000000000..ac2d1aff9 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/eo_001.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/es.dat b/venv/lib/python3.10/site-packages/babel/locale-data/es.dat new file mode 100644 index 000000000..5473aa8ce Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/es.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/es_419.dat b/venv/lib/python3.10/site-packages/babel/locale-data/es_419.dat new file mode 100644 index 000000000..610660bfd Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/es_419.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/es_AR.dat b/venv/lib/python3.10/site-packages/babel/locale-data/es_AR.dat new file mode 100644 index 000000000..ba0a88cfe Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/es_AR.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/es_BO.dat b/venv/lib/python3.10/site-packages/babel/locale-data/es_BO.dat new file mode 100644 index 000000000..ffcabf5db Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/es_BO.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/es_BR.dat b/venv/lib/python3.10/site-packages/babel/locale-data/es_BR.dat new file mode 100644 index 000000000..da7adf9ba Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/es_BR.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/es_BZ.dat b/venv/lib/python3.10/site-packages/babel/locale-data/es_BZ.dat new file mode 100644 index 000000000..a4b2bb534 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/es_BZ.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/es_CL.dat b/venv/lib/python3.10/site-packages/babel/locale-data/es_CL.dat new file mode 100644 index 000000000..ee490ea01 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/es_CL.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/es_CO.dat b/venv/lib/python3.10/site-packages/babel/locale-data/es_CO.dat new file mode 100644 index 000000000..b29cec2cf Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/es_CO.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/es_CR.dat b/venv/lib/python3.10/site-packages/babel/locale-data/es_CR.dat new file mode 100644 index 000000000..dcfe1ea95 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/es_CR.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/es_CU.dat b/venv/lib/python3.10/site-packages/babel/locale-data/es_CU.dat new file mode 100644 index 000000000..036177c67 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/es_CU.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/es_DO.dat b/venv/lib/python3.10/site-packages/babel/locale-data/es_DO.dat new file mode 100644 index 000000000..97c968e04 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/es_DO.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/es_EA.dat b/venv/lib/python3.10/site-packages/babel/locale-data/es_EA.dat new file mode 100644 index 000000000..8faa702b1 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/es_EA.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/es_EC.dat b/venv/lib/python3.10/site-packages/babel/locale-data/es_EC.dat new file mode 100644 index 000000000..4310786e6 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/es_EC.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/es_ES.dat b/venv/lib/python3.10/site-packages/babel/locale-data/es_ES.dat new file mode 100644 index 000000000..99fcd016b Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/es_ES.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/es_GQ.dat b/venv/lib/python3.10/site-packages/babel/locale-data/es_GQ.dat new file mode 100644 index 000000000..a95675cd4 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/es_GQ.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/es_GT.dat b/venv/lib/python3.10/site-packages/babel/locale-data/es_GT.dat new file mode 100644 index 000000000..42cd10062 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/es_GT.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/es_HN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/es_HN.dat new file mode 100644 index 000000000..922194419 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/es_HN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/es_IC.dat b/venv/lib/python3.10/site-packages/babel/locale-data/es_IC.dat new file mode 100644 index 000000000..4820e98db Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/es_IC.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/es_MX.dat b/venv/lib/python3.10/site-packages/babel/locale-data/es_MX.dat new file mode 100644 index 000000000..dd357ec62 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/es_MX.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/es_NI.dat b/venv/lib/python3.10/site-packages/babel/locale-data/es_NI.dat new file mode 100644 index 000000000..5c698f5e5 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/es_NI.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/es_PA.dat b/venv/lib/python3.10/site-packages/babel/locale-data/es_PA.dat new file mode 100644 index 000000000..e740f38e4 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/es_PA.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/es_PE.dat b/venv/lib/python3.10/site-packages/babel/locale-data/es_PE.dat new file mode 100644 index 000000000..14a22ab9a Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/es_PE.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/es_PH.dat b/venv/lib/python3.10/site-packages/babel/locale-data/es_PH.dat new file mode 100644 index 000000000..5cae53b2f Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/es_PH.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/es_PR.dat b/venv/lib/python3.10/site-packages/babel/locale-data/es_PR.dat new file mode 100644 index 000000000..d0d8ecf80 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/es_PR.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/es_PY.dat b/venv/lib/python3.10/site-packages/babel/locale-data/es_PY.dat new file mode 100644 index 000000000..27be15201 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/es_PY.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/es_SV.dat b/venv/lib/python3.10/site-packages/babel/locale-data/es_SV.dat new file mode 100644 index 000000000..f9257f500 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/es_SV.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/es_US.dat b/venv/lib/python3.10/site-packages/babel/locale-data/es_US.dat new file mode 100644 index 000000000..8209e5b12 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/es_US.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/es_UY.dat b/venv/lib/python3.10/site-packages/babel/locale-data/es_UY.dat new file mode 100644 index 000000000..bf5f3ed07 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/es_UY.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/es_VE.dat b/venv/lib/python3.10/site-packages/babel/locale-data/es_VE.dat new file mode 100644 index 000000000..e0e7f6457 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/es_VE.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/et.dat b/venv/lib/python3.10/site-packages/babel/locale-data/et.dat new file mode 100644 index 000000000..6e5a6c902 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/et.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/et_EE.dat b/venv/lib/python3.10/site-packages/babel/locale-data/et_EE.dat new file mode 100644 index 000000000..665202d3a Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/et_EE.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/eu.dat b/venv/lib/python3.10/site-packages/babel/locale-data/eu.dat new file mode 100644 index 000000000..a6ac19701 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/eu.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/eu_ES.dat b/venv/lib/python3.10/site-packages/babel/locale-data/eu_ES.dat new file mode 100644 index 000000000..ae93f2630 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/eu_ES.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ewo.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ewo.dat new file mode 100644 index 000000000..7517f6f9f Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ewo.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ewo_CM.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ewo_CM.dat new file mode 100644 index 000000000..db3fc2d96 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ewo_CM.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fa.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fa.dat new file mode 100644 index 000000000..92aa5e629 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fa.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fa_AF.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fa_AF.dat new file mode 100644 index 000000000..205ab04c3 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fa_AF.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fa_IR.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fa_IR.dat new file mode 100644 index 000000000..ceeecf81a Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fa_IR.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ff.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ff.dat new file mode 100644 index 000000000..c85da1268 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ff.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ff_Adlm.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Adlm.dat new file mode 100644 index 000000000..e7c6cb738 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Adlm.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ff_Adlm_BF.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Adlm_BF.dat new file mode 100644 index 000000000..4a8e04d38 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Adlm_BF.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ff_Adlm_CM.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Adlm_CM.dat new file mode 100644 index 000000000..d7cfcb56c Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Adlm_CM.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ff_Adlm_GH.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Adlm_GH.dat new file mode 100644 index 000000000..e41003f35 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Adlm_GH.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ff_Adlm_GM.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Adlm_GM.dat new file mode 100644 index 000000000..258004a01 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Adlm_GM.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ff_Adlm_GN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Adlm_GN.dat new file mode 100644 index 000000000..cc554e728 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Adlm_GN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ff_Adlm_GW.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Adlm_GW.dat new file mode 100644 index 000000000..0fbc57b96 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Adlm_GW.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ff_Adlm_LR.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Adlm_LR.dat new file mode 100644 index 000000000..40e997704 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Adlm_LR.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ff_Adlm_MR.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Adlm_MR.dat new file mode 100644 index 000000000..0ef73bf39 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Adlm_MR.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ff_Adlm_NE.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Adlm_NE.dat new file mode 100644 index 000000000..1ebfe8222 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Adlm_NE.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ff_Adlm_NG.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Adlm_NG.dat new file mode 100644 index 000000000..3117c2792 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Adlm_NG.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ff_Adlm_SL.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Adlm_SL.dat new file mode 100644 index 000000000..9cd2512dc Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Adlm_SL.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ff_Adlm_SN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Adlm_SN.dat new file mode 100644 index 000000000..1a709b917 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Adlm_SN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ff_Latn.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Latn.dat new file mode 100644 index 000000000..6280fc3e8 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Latn.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ff_Latn_BF.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Latn_BF.dat new file mode 100644 index 000000000..f0fe2f07d Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Latn_BF.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ff_Latn_CM.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Latn_CM.dat new file mode 100644 index 000000000..08cf09cc5 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Latn_CM.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ff_Latn_GH.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Latn_GH.dat new file mode 100644 index 000000000..05dfa95b0 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Latn_GH.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ff_Latn_GM.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Latn_GM.dat new file mode 100644 index 000000000..95f8da2ab Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Latn_GM.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ff_Latn_GN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Latn_GN.dat new file mode 100644 index 000000000..f3ee9cc14 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Latn_GN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ff_Latn_GW.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Latn_GW.dat new file mode 100644 index 000000000..fafde8962 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Latn_GW.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ff_Latn_LR.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Latn_LR.dat new file mode 100644 index 000000000..1a7a0577f Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Latn_LR.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ff_Latn_MR.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Latn_MR.dat new file mode 100644 index 000000000..24b592627 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Latn_MR.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ff_Latn_NE.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Latn_NE.dat new file mode 100644 index 000000000..0b9a2a2aa Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Latn_NE.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ff_Latn_NG.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Latn_NG.dat new file mode 100644 index 000000000..eeeaa51b2 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Latn_NG.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ff_Latn_SL.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Latn_SL.dat new file mode 100644 index 000000000..382ff3a2d Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Latn_SL.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ff_Latn_SN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Latn_SN.dat new file mode 100644 index 000000000..16c7275cf Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ff_Latn_SN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fi.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fi.dat new file mode 100644 index 000000000..6cf61e82f Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fi.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fi_FI.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fi_FI.dat new file mode 100644 index 000000000..c6f723bc8 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fi_FI.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fil.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fil.dat new file mode 100644 index 000000000..520510826 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fil.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fil_PH.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fil_PH.dat new file mode 100644 index 000000000..1cac4bce6 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fil_PH.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fo.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fo.dat new file mode 100644 index 000000000..4f129cef4 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fo.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fo_DK.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fo_DK.dat new file mode 100644 index 000000000..ced96d949 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fo_DK.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fo_FO.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fo_FO.dat new file mode 100644 index 000000000..f55f5d94f Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fo_FO.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fr.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fr.dat new file mode 100644 index 000000000..56fff0d9b Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fr.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fr_BE.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fr_BE.dat new file mode 100644 index 000000000..e055c4797 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fr_BE.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fr_BF.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fr_BF.dat new file mode 100644 index 000000000..a972a7baa Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fr_BF.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fr_BI.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fr_BI.dat new file mode 100644 index 000000000..160ca0595 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fr_BI.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fr_BJ.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fr_BJ.dat new file mode 100644 index 000000000..fed31e208 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fr_BJ.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fr_BL.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fr_BL.dat new file mode 100644 index 000000000..46bbabe4f Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fr_BL.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fr_CA.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fr_CA.dat new file mode 100644 index 000000000..ea834cb9c Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fr_CA.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fr_CD.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fr_CD.dat new file mode 100644 index 000000000..e15d4056e Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fr_CD.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fr_CF.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fr_CF.dat new file mode 100644 index 000000000..ff84dd9ea Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fr_CF.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fr_CG.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fr_CG.dat new file mode 100644 index 000000000..94ae9809a Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fr_CG.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fr_CH.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fr_CH.dat new file mode 100644 index 000000000..0be2c9d8a Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fr_CH.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fr_CI.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fr_CI.dat new file mode 100644 index 000000000..504d6ddc0 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fr_CI.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fr_CM.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fr_CM.dat new file mode 100644 index 000000000..73cf0ae31 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fr_CM.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fr_DJ.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fr_DJ.dat new file mode 100644 index 000000000..134909bd9 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fr_DJ.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fr_DZ.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fr_DZ.dat new file mode 100644 index 000000000..2629a883a Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fr_DZ.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fr_FR.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fr_FR.dat new file mode 100644 index 000000000..312e8c85f Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fr_FR.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fr_GA.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fr_GA.dat new file mode 100644 index 000000000..42b9a4077 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fr_GA.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fr_GF.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fr_GF.dat new file mode 100644 index 000000000..d546df110 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fr_GF.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fr_GN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fr_GN.dat new file mode 100644 index 000000000..258a1e3fa Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fr_GN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fr_GP.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fr_GP.dat new file mode 100644 index 000000000..0a2a57297 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fr_GP.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fr_GQ.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fr_GQ.dat new file mode 100644 index 000000000..d7acf13f9 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fr_GQ.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fr_HT.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fr_HT.dat new file mode 100644 index 000000000..10a602144 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fr_HT.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fr_KM.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fr_KM.dat new file mode 100644 index 000000000..11aa92171 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fr_KM.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fr_LU.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fr_LU.dat new file mode 100644 index 000000000..2d6b00ae9 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fr_LU.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fr_MA.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fr_MA.dat new file mode 100644 index 000000000..f7e55a9f6 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fr_MA.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fr_MC.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fr_MC.dat new file mode 100644 index 000000000..a79467005 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fr_MC.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fr_MF.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fr_MF.dat new file mode 100644 index 000000000..7ce27e1e9 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fr_MF.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fr_MG.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fr_MG.dat new file mode 100644 index 000000000..fd3f37016 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fr_MG.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fr_ML.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fr_ML.dat new file mode 100644 index 000000000..22de67c1b Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fr_ML.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fr_MQ.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fr_MQ.dat new file mode 100644 index 000000000..9f2676b1e Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fr_MQ.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fr_MR.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fr_MR.dat new file mode 100644 index 000000000..7dfbdcd0d Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fr_MR.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fr_MU.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fr_MU.dat new file mode 100644 index 000000000..83d9aa2a8 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fr_MU.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fr_NC.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fr_NC.dat new file mode 100644 index 000000000..082d1c634 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fr_NC.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fr_NE.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fr_NE.dat new file mode 100644 index 000000000..2b73188f4 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fr_NE.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fr_PF.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fr_PF.dat new file mode 100644 index 000000000..a953a11d3 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fr_PF.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fr_PM.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fr_PM.dat new file mode 100644 index 000000000..002bd6b0a Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fr_PM.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fr_RE.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fr_RE.dat new file mode 100644 index 000000000..b485ea3a2 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fr_RE.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fr_RW.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fr_RW.dat new file mode 100644 index 000000000..648bf3cff Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fr_RW.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fr_SC.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fr_SC.dat new file mode 100644 index 000000000..d4ee8fdaf Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fr_SC.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fr_SN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fr_SN.dat new file mode 100644 index 000000000..de9817bf9 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fr_SN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fr_SY.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fr_SY.dat new file mode 100644 index 000000000..b14d29a6d Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fr_SY.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fr_TD.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fr_TD.dat new file mode 100644 index 000000000..9f3cfd5c0 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fr_TD.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fr_TG.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fr_TG.dat new file mode 100644 index 000000000..8e05c8852 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fr_TG.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fr_TN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fr_TN.dat new file mode 100644 index 000000000..caaabaf71 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fr_TN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fr_VU.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fr_VU.dat new file mode 100644 index 000000000..217aeaf5e Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fr_VU.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fr_WF.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fr_WF.dat new file mode 100644 index 000000000..15cff3718 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fr_WF.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fr_YT.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fr_YT.dat new file mode 100644 index 000000000..701676bcb Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fr_YT.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/frr.dat b/venv/lib/python3.10/site-packages/babel/locale-data/frr.dat new file mode 100644 index 000000000..f779d15a5 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/frr.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/frr_DE.dat b/venv/lib/python3.10/site-packages/babel/locale-data/frr_DE.dat new file mode 100644 index 000000000..35bc7bf7c Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/frr_DE.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fur.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fur.dat new file mode 100644 index 000000000..4b7e4ffae Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fur.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fur_IT.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fur_IT.dat new file mode 100644 index 000000000..b90067236 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fur_IT.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fy.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fy.dat new file mode 100644 index 000000000..ece67cb20 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fy.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/fy_NL.dat b/venv/lib/python3.10/site-packages/babel/locale-data/fy_NL.dat new file mode 100644 index 000000000..3578521d5 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/fy_NL.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ga.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ga.dat new file mode 100644 index 000000000..24faf33ed Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ga.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ga_GB.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ga_GB.dat new file mode 100644 index 000000000..27cc2b99d Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ga_GB.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ga_IE.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ga_IE.dat new file mode 100644 index 000000000..ef528394f Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ga_IE.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/gd.dat b/venv/lib/python3.10/site-packages/babel/locale-data/gd.dat new file mode 100644 index 000000000..38429e348 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/gd.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/gd_GB.dat b/venv/lib/python3.10/site-packages/babel/locale-data/gd_GB.dat new file mode 100644 index 000000000..75c010fb7 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/gd_GB.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/gl.dat b/venv/lib/python3.10/site-packages/babel/locale-data/gl.dat new file mode 100644 index 000000000..9729e27ad Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/gl.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/gl_ES.dat b/venv/lib/python3.10/site-packages/babel/locale-data/gl_ES.dat new file mode 100644 index 000000000..ca376aa3a Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/gl_ES.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/gsw.dat b/venv/lib/python3.10/site-packages/babel/locale-data/gsw.dat new file mode 100644 index 000000000..a785d14f4 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/gsw.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/gsw_CH.dat b/venv/lib/python3.10/site-packages/babel/locale-data/gsw_CH.dat new file mode 100644 index 000000000..f334d3bc7 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/gsw_CH.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/gsw_FR.dat b/venv/lib/python3.10/site-packages/babel/locale-data/gsw_FR.dat new file mode 100644 index 000000000..15b10b8b9 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/gsw_FR.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/gsw_LI.dat b/venv/lib/python3.10/site-packages/babel/locale-data/gsw_LI.dat new file mode 100644 index 000000000..08995c0f8 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/gsw_LI.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/gu.dat b/venv/lib/python3.10/site-packages/babel/locale-data/gu.dat new file mode 100644 index 000000000..fe9435a11 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/gu.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/gu_IN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/gu_IN.dat new file mode 100644 index 000000000..f6c29318d Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/gu_IN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/guz.dat b/venv/lib/python3.10/site-packages/babel/locale-data/guz.dat new file mode 100644 index 000000000..c2bd6f979 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/guz.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/guz_KE.dat b/venv/lib/python3.10/site-packages/babel/locale-data/guz_KE.dat new file mode 100644 index 000000000..4e4255d79 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/guz_KE.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/gv.dat b/venv/lib/python3.10/site-packages/babel/locale-data/gv.dat new file mode 100644 index 000000000..51fb313da Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/gv.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/gv_IM.dat b/venv/lib/python3.10/site-packages/babel/locale-data/gv_IM.dat new file mode 100644 index 000000000..744d6e69e Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/gv_IM.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ha.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ha.dat new file mode 100644 index 000000000..eb07830f4 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ha.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ha_GH.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ha_GH.dat new file mode 100644 index 000000000..65954b5d8 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ha_GH.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ha_NE.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ha_NE.dat new file mode 100644 index 000000000..7c5651962 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ha_NE.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ha_NG.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ha_NG.dat new file mode 100644 index 000000000..3209c594b Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ha_NG.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/haw.dat b/venv/lib/python3.10/site-packages/babel/locale-data/haw.dat new file mode 100644 index 000000000..76256b5a3 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/haw.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/haw_US.dat b/venv/lib/python3.10/site-packages/babel/locale-data/haw_US.dat new file mode 100644 index 000000000..1e2f497b0 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/haw_US.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/he.dat b/venv/lib/python3.10/site-packages/babel/locale-data/he.dat new file mode 100644 index 000000000..f43330c0e Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/he.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/he_IL.dat b/venv/lib/python3.10/site-packages/babel/locale-data/he_IL.dat new file mode 100644 index 000000000..c51973bf2 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/he_IL.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/hi.dat b/venv/lib/python3.10/site-packages/babel/locale-data/hi.dat new file mode 100644 index 000000000..d98973e17 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/hi.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/hi_IN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/hi_IN.dat new file mode 100644 index 000000000..f63c61589 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/hi_IN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/hi_Latn.dat b/venv/lib/python3.10/site-packages/babel/locale-data/hi_Latn.dat new file mode 100644 index 000000000..20184334f Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/hi_Latn.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/hi_Latn_IN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/hi_Latn_IN.dat new file mode 100644 index 000000000..f63c61589 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/hi_Latn_IN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/hr.dat b/venv/lib/python3.10/site-packages/babel/locale-data/hr.dat new file mode 100644 index 000000000..ff828c28d Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/hr.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/hr_BA.dat b/venv/lib/python3.10/site-packages/babel/locale-data/hr_BA.dat new file mode 100644 index 000000000..8b9655124 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/hr_BA.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/hr_HR.dat b/venv/lib/python3.10/site-packages/babel/locale-data/hr_HR.dat new file mode 100644 index 000000000..a6affba17 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/hr_HR.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/hsb.dat b/venv/lib/python3.10/site-packages/babel/locale-data/hsb.dat new file mode 100644 index 000000000..c1eb63622 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/hsb.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/hsb_DE.dat b/venv/lib/python3.10/site-packages/babel/locale-data/hsb_DE.dat new file mode 100644 index 000000000..56322e2f8 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/hsb_DE.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/hu.dat b/venv/lib/python3.10/site-packages/babel/locale-data/hu.dat new file mode 100644 index 000000000..87a2817c7 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/hu.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/hu_HU.dat b/venv/lib/python3.10/site-packages/babel/locale-data/hu_HU.dat new file mode 100644 index 000000000..8bedfed01 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/hu_HU.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/hy.dat b/venv/lib/python3.10/site-packages/babel/locale-data/hy.dat new file mode 100644 index 000000000..c184c1f93 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/hy.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/hy_AM.dat b/venv/lib/python3.10/site-packages/babel/locale-data/hy_AM.dat new file mode 100644 index 000000000..43e5574be Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/hy_AM.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ia.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ia.dat new file mode 100644 index 000000000..742f021e5 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ia.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ia_001.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ia_001.dat new file mode 100644 index 000000000..070647f5f Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ia_001.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/id.dat b/venv/lib/python3.10/site-packages/babel/locale-data/id.dat new file mode 100644 index 000000000..1aa71dd1c Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/id.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/id_ID.dat b/venv/lib/python3.10/site-packages/babel/locale-data/id_ID.dat new file mode 100644 index 000000000..d22d10f31 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/id_ID.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ig.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ig.dat new file mode 100644 index 000000000..64e96f733 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ig.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ig_NG.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ig_NG.dat new file mode 100644 index 000000000..3ea7e4efd Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ig_NG.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ii.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ii.dat new file mode 100644 index 000000000..253572689 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ii.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ii_CN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ii_CN.dat new file mode 100644 index 000000000..097308e82 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ii_CN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/is.dat b/venv/lib/python3.10/site-packages/babel/locale-data/is.dat new file mode 100644 index 000000000..681a977d6 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/is.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/is_IS.dat b/venv/lib/python3.10/site-packages/babel/locale-data/is_IS.dat new file mode 100644 index 000000000..7e77fb1d8 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/is_IS.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/it.dat b/venv/lib/python3.10/site-packages/babel/locale-data/it.dat new file mode 100644 index 000000000..1f4c5a569 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/it.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/it_CH.dat b/venv/lib/python3.10/site-packages/babel/locale-data/it_CH.dat new file mode 100644 index 000000000..3c6ca1fc0 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/it_CH.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/it_IT.dat b/venv/lib/python3.10/site-packages/babel/locale-data/it_IT.dat new file mode 100644 index 000000000..8a5f27742 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/it_IT.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/it_SM.dat b/venv/lib/python3.10/site-packages/babel/locale-data/it_SM.dat new file mode 100644 index 000000000..6c4c23c8d Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/it_SM.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/it_VA.dat b/venv/lib/python3.10/site-packages/babel/locale-data/it_VA.dat new file mode 100644 index 000000000..3e308d31c Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/it_VA.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ja.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ja.dat new file mode 100644 index 000000000..37fb2134b Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ja.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ja_JP.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ja_JP.dat new file mode 100644 index 000000000..be93fcc81 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ja_JP.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/jgo.dat b/venv/lib/python3.10/site-packages/babel/locale-data/jgo.dat new file mode 100644 index 000000000..e2bb7489f Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/jgo.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/jgo_CM.dat b/venv/lib/python3.10/site-packages/babel/locale-data/jgo_CM.dat new file mode 100644 index 000000000..c012c50e3 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/jgo_CM.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/jmc.dat b/venv/lib/python3.10/site-packages/babel/locale-data/jmc.dat new file mode 100644 index 000000000..55674593d Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/jmc.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/jmc_TZ.dat b/venv/lib/python3.10/site-packages/babel/locale-data/jmc_TZ.dat new file mode 100644 index 000000000..2a1942f44 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/jmc_TZ.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/jv.dat b/venv/lib/python3.10/site-packages/babel/locale-data/jv.dat new file mode 100644 index 000000000..a82038158 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/jv.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/jv_ID.dat b/venv/lib/python3.10/site-packages/babel/locale-data/jv_ID.dat new file mode 100644 index 000000000..42926ef5a Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/jv_ID.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ka.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ka.dat new file mode 100644 index 000000000..04f4c1fd0 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ka.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ka_GE.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ka_GE.dat new file mode 100644 index 000000000..f21bf118d Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ka_GE.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/kab.dat b/venv/lib/python3.10/site-packages/babel/locale-data/kab.dat new file mode 100644 index 000000000..188023a1a Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/kab.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/kab_DZ.dat b/venv/lib/python3.10/site-packages/babel/locale-data/kab_DZ.dat new file mode 100644 index 000000000..d36214bee Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/kab_DZ.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/kam.dat b/venv/lib/python3.10/site-packages/babel/locale-data/kam.dat new file mode 100644 index 000000000..20eb76785 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/kam.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/kam_KE.dat b/venv/lib/python3.10/site-packages/babel/locale-data/kam_KE.dat new file mode 100644 index 000000000..dd7f5b4eb Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/kam_KE.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/kde.dat b/venv/lib/python3.10/site-packages/babel/locale-data/kde.dat new file mode 100644 index 000000000..77141c922 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/kde.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/kde_TZ.dat b/venv/lib/python3.10/site-packages/babel/locale-data/kde_TZ.dat new file mode 100644 index 000000000..3361937ce Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/kde_TZ.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/kea.dat b/venv/lib/python3.10/site-packages/babel/locale-data/kea.dat new file mode 100644 index 000000000..70e51b8e0 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/kea.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/kea_CV.dat b/venv/lib/python3.10/site-packages/babel/locale-data/kea_CV.dat new file mode 100644 index 000000000..a605c759b Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/kea_CV.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/kgp.dat b/venv/lib/python3.10/site-packages/babel/locale-data/kgp.dat new file mode 100644 index 000000000..165effdc5 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/kgp.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/kgp_BR.dat b/venv/lib/python3.10/site-packages/babel/locale-data/kgp_BR.dat new file mode 100644 index 000000000..5c656235b Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/kgp_BR.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/khq.dat b/venv/lib/python3.10/site-packages/babel/locale-data/khq.dat new file mode 100644 index 000000000..e2de27be4 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/khq.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/khq_ML.dat b/venv/lib/python3.10/site-packages/babel/locale-data/khq_ML.dat new file mode 100644 index 000000000..fb52fbec3 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/khq_ML.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ki.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ki.dat new file mode 100644 index 000000000..2fe7121ba Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ki.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ki_KE.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ki_KE.dat new file mode 100644 index 000000000..0019c6f6a Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ki_KE.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/kk.dat b/venv/lib/python3.10/site-packages/babel/locale-data/kk.dat new file mode 100644 index 000000000..151ccbe63 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/kk.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/kk_KZ.dat b/venv/lib/python3.10/site-packages/babel/locale-data/kk_KZ.dat new file mode 100644 index 000000000..463855cd6 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/kk_KZ.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/kkj.dat b/venv/lib/python3.10/site-packages/babel/locale-data/kkj.dat new file mode 100644 index 000000000..ebc51de9b Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/kkj.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/kkj_CM.dat b/venv/lib/python3.10/site-packages/babel/locale-data/kkj_CM.dat new file mode 100644 index 000000000..707a86d73 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/kkj_CM.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/kl.dat b/venv/lib/python3.10/site-packages/babel/locale-data/kl.dat new file mode 100644 index 000000000..a3abdacd6 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/kl.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/kl_GL.dat b/venv/lib/python3.10/site-packages/babel/locale-data/kl_GL.dat new file mode 100644 index 000000000..222744f4b Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/kl_GL.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/kln.dat b/venv/lib/python3.10/site-packages/babel/locale-data/kln.dat new file mode 100644 index 000000000..9a2bebe3a Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/kln.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/kln_KE.dat b/venv/lib/python3.10/site-packages/babel/locale-data/kln_KE.dat new file mode 100644 index 000000000..479542f4f Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/kln_KE.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/km.dat b/venv/lib/python3.10/site-packages/babel/locale-data/km.dat new file mode 100644 index 000000000..b5d165a43 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/km.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/km_KH.dat b/venv/lib/python3.10/site-packages/babel/locale-data/km_KH.dat new file mode 100644 index 000000000..db172fea0 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/km_KH.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/kn.dat b/venv/lib/python3.10/site-packages/babel/locale-data/kn.dat new file mode 100644 index 000000000..343660b69 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/kn.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/kn_IN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/kn_IN.dat new file mode 100644 index 000000000..bbf978f8b Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/kn_IN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ko.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ko.dat new file mode 100644 index 000000000..fb2c74eff Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ko.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ko_KP.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ko_KP.dat new file mode 100644 index 000000000..79b8c4ce0 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ko_KP.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ko_KR.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ko_KR.dat new file mode 100644 index 000000000..fc8967907 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ko_KR.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/kok.dat b/venv/lib/python3.10/site-packages/babel/locale-data/kok.dat new file mode 100644 index 000000000..2e71c4113 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/kok.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/kok_IN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/kok_IN.dat new file mode 100644 index 000000000..3cb3a25fc Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/kok_IN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ks.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ks.dat new file mode 100644 index 000000000..c75b323bf Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ks.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ks_Arab.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ks_Arab.dat new file mode 100644 index 000000000..fcf3d3059 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ks_Arab.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ks_Arab_IN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ks_Arab_IN.dat new file mode 100644 index 000000000..f068e1fca Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ks_Arab_IN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ks_Deva.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ks_Deva.dat new file mode 100644 index 000000000..1a596262e Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ks_Deva.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ks_Deva_IN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ks_Deva_IN.dat new file mode 100644 index 000000000..f068e1fca Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ks_Deva_IN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ksb.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ksb.dat new file mode 100644 index 000000000..053e8a09e Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ksb.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ksb_TZ.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ksb_TZ.dat new file mode 100644 index 000000000..f16d8e634 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ksb_TZ.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ksf.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ksf.dat new file mode 100644 index 000000000..4f010da6c Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ksf.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ksf_CM.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ksf_CM.dat new file mode 100644 index 000000000..7b9c44d2c Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ksf_CM.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ksh.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ksh.dat new file mode 100644 index 000000000..b2e8f28ed Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ksh.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ksh_DE.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ksh_DE.dat new file mode 100644 index 000000000..94684c296 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ksh_DE.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ku.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ku.dat new file mode 100644 index 000000000..72a2682c8 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ku.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ku_TR.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ku_TR.dat new file mode 100644 index 000000000..bb52a649d Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ku_TR.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/kw.dat b/venv/lib/python3.10/site-packages/babel/locale-data/kw.dat new file mode 100644 index 000000000..78ca2a2a5 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/kw.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/kw_GB.dat b/venv/lib/python3.10/site-packages/babel/locale-data/kw_GB.dat new file mode 100644 index 000000000..a9a68be7e Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/kw_GB.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ky.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ky.dat new file mode 100644 index 000000000..9cae66880 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ky.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ky_KG.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ky_KG.dat new file mode 100644 index 000000000..df59b77e3 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ky_KG.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/lag.dat b/venv/lib/python3.10/site-packages/babel/locale-data/lag.dat new file mode 100644 index 000000000..cf395bd3d Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/lag.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/lag_TZ.dat b/venv/lib/python3.10/site-packages/babel/locale-data/lag_TZ.dat new file mode 100644 index 000000000..87c833818 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/lag_TZ.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/lb.dat b/venv/lib/python3.10/site-packages/babel/locale-data/lb.dat new file mode 100644 index 000000000..074dbe9b4 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/lb.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/lb_LU.dat b/venv/lib/python3.10/site-packages/babel/locale-data/lb_LU.dat new file mode 100644 index 000000000..c9862881a Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/lb_LU.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/lg.dat b/venv/lib/python3.10/site-packages/babel/locale-data/lg.dat new file mode 100644 index 000000000..03c8fa11f Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/lg.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/lg_UG.dat b/venv/lib/python3.10/site-packages/babel/locale-data/lg_UG.dat new file mode 100644 index 000000000..b3922af96 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/lg_UG.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/lkt.dat b/venv/lib/python3.10/site-packages/babel/locale-data/lkt.dat new file mode 100644 index 000000000..69838d810 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/lkt.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/lkt_US.dat b/venv/lib/python3.10/site-packages/babel/locale-data/lkt_US.dat new file mode 100644 index 000000000..ebfe5ee53 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/lkt_US.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ln.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ln.dat new file mode 100644 index 000000000..13de07f66 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ln.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ln_AO.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ln_AO.dat new file mode 100644 index 000000000..b4c748ed3 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ln_AO.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ln_CD.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ln_CD.dat new file mode 100644 index 000000000..6c0dcefdc Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ln_CD.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ln_CF.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ln_CF.dat new file mode 100644 index 000000000..6d70e1c41 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ln_CF.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ln_CG.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ln_CG.dat new file mode 100644 index 000000000..34abe4ef5 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ln_CG.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/lo.dat b/venv/lib/python3.10/site-packages/babel/locale-data/lo.dat new file mode 100644 index 000000000..295e07958 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/lo.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/lo_LA.dat b/venv/lib/python3.10/site-packages/babel/locale-data/lo_LA.dat new file mode 100644 index 000000000..5452765f0 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/lo_LA.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/lrc.dat b/venv/lib/python3.10/site-packages/babel/locale-data/lrc.dat new file mode 100644 index 000000000..74980fe3e Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/lrc.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/lrc_IQ.dat b/venv/lib/python3.10/site-packages/babel/locale-data/lrc_IQ.dat new file mode 100644 index 000000000..212f8d591 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/lrc_IQ.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/lrc_IR.dat b/venv/lib/python3.10/site-packages/babel/locale-data/lrc_IR.dat new file mode 100644 index 000000000..997d078f1 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/lrc_IR.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/lt.dat b/venv/lib/python3.10/site-packages/babel/locale-data/lt.dat new file mode 100644 index 000000000..9ad1e0b1d Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/lt.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/lt_LT.dat b/venv/lib/python3.10/site-packages/babel/locale-data/lt_LT.dat new file mode 100644 index 000000000..b161ec3e1 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/lt_LT.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/lu.dat b/venv/lib/python3.10/site-packages/babel/locale-data/lu.dat new file mode 100644 index 000000000..8405e12b7 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/lu.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/lu_CD.dat b/venv/lib/python3.10/site-packages/babel/locale-data/lu_CD.dat new file mode 100644 index 000000000..02d48cbba Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/lu_CD.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/luo.dat b/venv/lib/python3.10/site-packages/babel/locale-data/luo.dat new file mode 100644 index 000000000..f64888185 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/luo.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/luo_KE.dat b/venv/lib/python3.10/site-packages/babel/locale-data/luo_KE.dat new file mode 100644 index 000000000..a9100187b Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/luo_KE.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/luy.dat b/venv/lib/python3.10/site-packages/babel/locale-data/luy.dat new file mode 100644 index 000000000..1a588b973 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/luy.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/luy_KE.dat b/venv/lib/python3.10/site-packages/babel/locale-data/luy_KE.dat new file mode 100644 index 000000000..712c25329 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/luy_KE.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/lv.dat b/venv/lib/python3.10/site-packages/babel/locale-data/lv.dat new file mode 100644 index 000000000..4dc7c5bbe Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/lv.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/lv_LV.dat b/venv/lib/python3.10/site-packages/babel/locale-data/lv_LV.dat new file mode 100644 index 000000000..8fe8bd33f Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/lv_LV.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/mai.dat b/venv/lib/python3.10/site-packages/babel/locale-data/mai.dat new file mode 100644 index 000000000..62a25ec14 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/mai.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/mai_IN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/mai_IN.dat new file mode 100644 index 000000000..e9607f709 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/mai_IN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/mas.dat b/venv/lib/python3.10/site-packages/babel/locale-data/mas.dat new file mode 100644 index 000000000..727e12f53 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/mas.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/mas_KE.dat b/venv/lib/python3.10/site-packages/babel/locale-data/mas_KE.dat new file mode 100644 index 000000000..930717f71 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/mas_KE.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/mas_TZ.dat b/venv/lib/python3.10/site-packages/babel/locale-data/mas_TZ.dat new file mode 100644 index 000000000..892f66112 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/mas_TZ.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/mdf.dat b/venv/lib/python3.10/site-packages/babel/locale-data/mdf.dat new file mode 100644 index 000000000..d3d54023e Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/mdf.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/mdf_RU.dat b/venv/lib/python3.10/site-packages/babel/locale-data/mdf_RU.dat new file mode 100644 index 000000000..399fd8704 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/mdf_RU.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/mer.dat b/venv/lib/python3.10/site-packages/babel/locale-data/mer.dat new file mode 100644 index 000000000..a3557f01b Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/mer.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/mer_KE.dat b/venv/lib/python3.10/site-packages/babel/locale-data/mer_KE.dat new file mode 100644 index 000000000..bd3dd8234 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/mer_KE.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/mfe.dat b/venv/lib/python3.10/site-packages/babel/locale-data/mfe.dat new file mode 100644 index 000000000..d969be268 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/mfe.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/mfe_MU.dat b/venv/lib/python3.10/site-packages/babel/locale-data/mfe_MU.dat new file mode 100644 index 000000000..d1a83e85a Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/mfe_MU.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/mg.dat b/venv/lib/python3.10/site-packages/babel/locale-data/mg.dat new file mode 100644 index 000000000..878dac302 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/mg.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/mg_MG.dat b/venv/lib/python3.10/site-packages/babel/locale-data/mg_MG.dat new file mode 100644 index 000000000..c6e3e5cb6 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/mg_MG.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/mgh.dat b/venv/lib/python3.10/site-packages/babel/locale-data/mgh.dat new file mode 100644 index 000000000..55e48e15f Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/mgh.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/mgh_MZ.dat b/venv/lib/python3.10/site-packages/babel/locale-data/mgh_MZ.dat new file mode 100644 index 000000000..737e355f1 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/mgh_MZ.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/mgo.dat b/venv/lib/python3.10/site-packages/babel/locale-data/mgo.dat new file mode 100644 index 000000000..c1842812e Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/mgo.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/mgo_CM.dat b/venv/lib/python3.10/site-packages/babel/locale-data/mgo_CM.dat new file mode 100644 index 000000000..4f08d3268 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/mgo_CM.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/mi.dat b/venv/lib/python3.10/site-packages/babel/locale-data/mi.dat new file mode 100644 index 000000000..26f73add8 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/mi.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/mi_NZ.dat b/venv/lib/python3.10/site-packages/babel/locale-data/mi_NZ.dat new file mode 100644 index 000000000..37e1a314c Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/mi_NZ.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/mk.dat b/venv/lib/python3.10/site-packages/babel/locale-data/mk.dat new file mode 100644 index 000000000..496c2492e Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/mk.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/mk_MK.dat b/venv/lib/python3.10/site-packages/babel/locale-data/mk_MK.dat new file mode 100644 index 000000000..07c67a396 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/mk_MK.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ml.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ml.dat new file mode 100644 index 000000000..eef797493 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ml.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ml_IN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ml_IN.dat new file mode 100644 index 000000000..aff879a58 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ml_IN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/mn.dat b/venv/lib/python3.10/site-packages/babel/locale-data/mn.dat new file mode 100644 index 000000000..7477aeb00 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/mn.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/mn_MN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/mn_MN.dat new file mode 100644 index 000000000..89e61d52d Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/mn_MN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/mni.dat b/venv/lib/python3.10/site-packages/babel/locale-data/mni.dat new file mode 100644 index 000000000..ec4b28db8 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/mni.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/mni_Beng.dat b/venv/lib/python3.10/site-packages/babel/locale-data/mni_Beng.dat new file mode 100644 index 000000000..6e20d2a6e Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/mni_Beng.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/mni_Beng_IN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/mni_Beng_IN.dat new file mode 100644 index 000000000..c03bced05 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/mni_Beng_IN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/mr.dat b/venv/lib/python3.10/site-packages/babel/locale-data/mr.dat new file mode 100644 index 000000000..0575f647e Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/mr.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/mr_IN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/mr_IN.dat new file mode 100644 index 000000000..50daea2e5 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/mr_IN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ms.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ms.dat new file mode 100644 index 000000000..4f5f9fcb2 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ms.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ms_BN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ms_BN.dat new file mode 100644 index 000000000..e18959969 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ms_BN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ms_ID.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ms_ID.dat new file mode 100644 index 000000000..f4f005237 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ms_ID.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ms_MY.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ms_MY.dat new file mode 100644 index 000000000..e9e39a557 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ms_MY.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ms_SG.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ms_SG.dat new file mode 100644 index 000000000..041cc678b Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ms_SG.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/mt.dat b/venv/lib/python3.10/site-packages/babel/locale-data/mt.dat new file mode 100644 index 000000000..35670de1b Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/mt.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/mt_MT.dat b/venv/lib/python3.10/site-packages/babel/locale-data/mt_MT.dat new file mode 100644 index 000000000..79462fc51 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/mt_MT.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/mua.dat b/venv/lib/python3.10/site-packages/babel/locale-data/mua.dat new file mode 100644 index 000000000..62ea381de Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/mua.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/mua_CM.dat b/venv/lib/python3.10/site-packages/babel/locale-data/mua_CM.dat new file mode 100644 index 000000000..0c408b888 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/mua_CM.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/my.dat b/venv/lib/python3.10/site-packages/babel/locale-data/my.dat new file mode 100644 index 000000000..4a1402691 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/my.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/my_MM.dat b/venv/lib/python3.10/site-packages/babel/locale-data/my_MM.dat new file mode 100644 index 000000000..8f391c17c Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/my_MM.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/mzn.dat b/venv/lib/python3.10/site-packages/babel/locale-data/mzn.dat new file mode 100644 index 000000000..3cc7a8d85 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/mzn.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/mzn_IR.dat b/venv/lib/python3.10/site-packages/babel/locale-data/mzn_IR.dat new file mode 100644 index 000000000..d924320e3 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/mzn_IR.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/naq.dat b/venv/lib/python3.10/site-packages/babel/locale-data/naq.dat new file mode 100644 index 000000000..0c4711606 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/naq.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/naq_NA.dat b/venv/lib/python3.10/site-packages/babel/locale-data/naq_NA.dat new file mode 100644 index 000000000..4b44e4e1f Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/naq_NA.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/nb.dat b/venv/lib/python3.10/site-packages/babel/locale-data/nb.dat new file mode 100644 index 000000000..184c82fca Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/nb.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/nb_NO.dat b/venv/lib/python3.10/site-packages/babel/locale-data/nb_NO.dat new file mode 100644 index 000000000..9239a120e Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/nb_NO.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/nb_SJ.dat b/venv/lib/python3.10/site-packages/babel/locale-data/nb_SJ.dat new file mode 100644 index 000000000..1125c06ca Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/nb_SJ.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/nd.dat b/venv/lib/python3.10/site-packages/babel/locale-data/nd.dat new file mode 100644 index 000000000..50f1ab767 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/nd.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/nd_ZW.dat b/venv/lib/python3.10/site-packages/babel/locale-data/nd_ZW.dat new file mode 100644 index 000000000..f5cc7ecd3 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/nd_ZW.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/nds.dat b/venv/lib/python3.10/site-packages/babel/locale-data/nds.dat new file mode 100644 index 000000000..81a977024 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/nds.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/nds_DE.dat b/venv/lib/python3.10/site-packages/babel/locale-data/nds_DE.dat new file mode 100644 index 000000000..5f7c48f12 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/nds_DE.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/nds_NL.dat b/venv/lib/python3.10/site-packages/babel/locale-data/nds_NL.dat new file mode 100644 index 000000000..6febe8beb Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/nds_NL.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ne.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ne.dat new file mode 100644 index 000000000..78490f435 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ne.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ne_IN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ne_IN.dat new file mode 100644 index 000000000..558d07e82 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ne_IN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ne_NP.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ne_NP.dat new file mode 100644 index 000000000..cc087afc0 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ne_NP.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/nl.dat b/venv/lib/python3.10/site-packages/babel/locale-data/nl.dat new file mode 100644 index 000000000..2544c39c9 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/nl.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/nl_AW.dat b/venv/lib/python3.10/site-packages/babel/locale-data/nl_AW.dat new file mode 100644 index 000000000..5409d84b4 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/nl_AW.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/nl_BE.dat b/venv/lib/python3.10/site-packages/babel/locale-data/nl_BE.dat new file mode 100644 index 000000000..1a81bb983 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/nl_BE.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/nl_BQ.dat b/venv/lib/python3.10/site-packages/babel/locale-data/nl_BQ.dat new file mode 100644 index 000000000..1697c58c1 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/nl_BQ.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/nl_CW.dat b/venv/lib/python3.10/site-packages/babel/locale-data/nl_CW.dat new file mode 100644 index 000000000..da23cb65c Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/nl_CW.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/nl_NL.dat b/venv/lib/python3.10/site-packages/babel/locale-data/nl_NL.dat new file mode 100644 index 000000000..051b0fe29 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/nl_NL.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/nl_SR.dat b/venv/lib/python3.10/site-packages/babel/locale-data/nl_SR.dat new file mode 100644 index 000000000..3d45a53bc Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/nl_SR.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/nl_SX.dat b/venv/lib/python3.10/site-packages/babel/locale-data/nl_SX.dat new file mode 100644 index 000000000..f4041e410 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/nl_SX.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/nmg.dat b/venv/lib/python3.10/site-packages/babel/locale-data/nmg.dat new file mode 100644 index 000000000..74a8a0932 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/nmg.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/nmg_CM.dat b/venv/lib/python3.10/site-packages/babel/locale-data/nmg_CM.dat new file mode 100644 index 000000000..3f85753b1 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/nmg_CM.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/nn.dat b/venv/lib/python3.10/site-packages/babel/locale-data/nn.dat new file mode 100644 index 000000000..72b52a3e6 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/nn.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/nn_NO.dat b/venv/lib/python3.10/site-packages/babel/locale-data/nn_NO.dat new file mode 100644 index 000000000..21105971a Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/nn_NO.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/nnh.dat b/venv/lib/python3.10/site-packages/babel/locale-data/nnh.dat new file mode 100644 index 000000000..967815953 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/nnh.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/nnh_CM.dat b/venv/lib/python3.10/site-packages/babel/locale-data/nnh_CM.dat new file mode 100644 index 000000000..35a138fed Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/nnh_CM.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/no.dat b/venv/lib/python3.10/site-packages/babel/locale-data/no.dat new file mode 100644 index 000000000..e8761ee8e Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/no.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/nus.dat b/venv/lib/python3.10/site-packages/babel/locale-data/nus.dat new file mode 100644 index 000000000..3a63d6a30 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/nus.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/nus_SS.dat b/venv/lib/python3.10/site-packages/babel/locale-data/nus_SS.dat new file mode 100644 index 000000000..b9b02a2f6 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/nus_SS.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/nyn.dat b/venv/lib/python3.10/site-packages/babel/locale-data/nyn.dat new file mode 100644 index 000000000..ef83b303e Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/nyn.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/nyn_UG.dat b/venv/lib/python3.10/site-packages/babel/locale-data/nyn_UG.dat new file mode 100644 index 000000000..459b56a09 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/nyn_UG.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/oc.dat b/venv/lib/python3.10/site-packages/babel/locale-data/oc.dat new file mode 100644 index 000000000..8149d9862 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/oc.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/oc_ES.dat b/venv/lib/python3.10/site-packages/babel/locale-data/oc_ES.dat new file mode 100644 index 000000000..bb5f34fa9 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/oc_ES.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/oc_FR.dat b/venv/lib/python3.10/site-packages/babel/locale-data/oc_FR.dat new file mode 100644 index 000000000..3bfccf7b0 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/oc_FR.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/om.dat b/venv/lib/python3.10/site-packages/babel/locale-data/om.dat new file mode 100644 index 000000000..2bddad06b Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/om.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/om_ET.dat b/venv/lib/python3.10/site-packages/babel/locale-data/om_ET.dat new file mode 100644 index 000000000..e515109cb Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/om_ET.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/om_KE.dat b/venv/lib/python3.10/site-packages/babel/locale-data/om_KE.dat new file mode 100644 index 000000000..5864b43b6 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/om_KE.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/or.dat b/venv/lib/python3.10/site-packages/babel/locale-data/or.dat new file mode 100644 index 000000000..ea3dd6996 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/or.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/or_IN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/or_IN.dat new file mode 100644 index 000000000..f22745a91 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/or_IN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/os.dat b/venv/lib/python3.10/site-packages/babel/locale-data/os.dat new file mode 100644 index 000000000..df4252627 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/os.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/os_GE.dat b/venv/lib/python3.10/site-packages/babel/locale-data/os_GE.dat new file mode 100644 index 000000000..221aa011e Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/os_GE.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/os_RU.dat b/venv/lib/python3.10/site-packages/babel/locale-data/os_RU.dat new file mode 100644 index 000000000..a1de82132 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/os_RU.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/pa.dat b/venv/lib/python3.10/site-packages/babel/locale-data/pa.dat new file mode 100644 index 000000000..a3153ebdf Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/pa.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/pa_Arab.dat b/venv/lib/python3.10/site-packages/babel/locale-data/pa_Arab.dat new file mode 100644 index 000000000..9405022ec Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/pa_Arab.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/pa_Arab_PK.dat b/venv/lib/python3.10/site-packages/babel/locale-data/pa_Arab_PK.dat new file mode 100644 index 000000000..5de5edf22 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/pa_Arab_PK.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/pa_Guru.dat b/venv/lib/python3.10/site-packages/babel/locale-data/pa_Guru.dat new file mode 100644 index 000000000..598c32a81 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/pa_Guru.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/pa_Guru_IN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/pa_Guru_IN.dat new file mode 100644 index 000000000..108663120 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/pa_Guru_IN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/pcm.dat b/venv/lib/python3.10/site-packages/babel/locale-data/pcm.dat new file mode 100644 index 000000000..3b8676900 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/pcm.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/pcm_NG.dat b/venv/lib/python3.10/site-packages/babel/locale-data/pcm_NG.dat new file mode 100644 index 000000000..2770f7e39 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/pcm_NG.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/pis.dat b/venv/lib/python3.10/site-packages/babel/locale-data/pis.dat new file mode 100644 index 000000000..1050392c0 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/pis.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/pis_SB.dat b/venv/lib/python3.10/site-packages/babel/locale-data/pis_SB.dat new file mode 100644 index 000000000..e8d0225fb Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/pis_SB.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/pl.dat b/venv/lib/python3.10/site-packages/babel/locale-data/pl.dat new file mode 100644 index 000000000..f2a88a8eb Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/pl.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/pl_PL.dat b/venv/lib/python3.10/site-packages/babel/locale-data/pl_PL.dat new file mode 100644 index 000000000..c308d4e49 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/pl_PL.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ps.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ps.dat new file mode 100644 index 000000000..89488d9c6 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ps.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ps_AF.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ps_AF.dat new file mode 100644 index 000000000..f83e20827 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ps_AF.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ps_PK.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ps_PK.dat new file mode 100644 index 000000000..219ca59f7 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ps_PK.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/pt.dat b/venv/lib/python3.10/site-packages/babel/locale-data/pt.dat new file mode 100644 index 000000000..d4cb2d15e Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/pt.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/pt_AO.dat b/venv/lib/python3.10/site-packages/babel/locale-data/pt_AO.dat new file mode 100644 index 000000000..c864d3809 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/pt_AO.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/pt_BR.dat b/venv/lib/python3.10/site-packages/babel/locale-data/pt_BR.dat new file mode 100644 index 000000000..1fee99b5c Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/pt_BR.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/pt_CH.dat b/venv/lib/python3.10/site-packages/babel/locale-data/pt_CH.dat new file mode 100644 index 000000000..6c76ea7e2 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/pt_CH.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/pt_CV.dat b/venv/lib/python3.10/site-packages/babel/locale-data/pt_CV.dat new file mode 100644 index 000000000..d9e8c6453 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/pt_CV.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/pt_GQ.dat b/venv/lib/python3.10/site-packages/babel/locale-data/pt_GQ.dat new file mode 100644 index 000000000..8debbd55e Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/pt_GQ.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/pt_GW.dat b/venv/lib/python3.10/site-packages/babel/locale-data/pt_GW.dat new file mode 100644 index 000000000..43fc98f3e Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/pt_GW.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/pt_LU.dat b/venv/lib/python3.10/site-packages/babel/locale-data/pt_LU.dat new file mode 100644 index 000000000..f9f59ec53 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/pt_LU.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/pt_MO.dat b/venv/lib/python3.10/site-packages/babel/locale-data/pt_MO.dat new file mode 100644 index 000000000..91d032b1b Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/pt_MO.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/pt_MZ.dat b/venv/lib/python3.10/site-packages/babel/locale-data/pt_MZ.dat new file mode 100644 index 000000000..7a234369b Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/pt_MZ.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/pt_PT.dat b/venv/lib/python3.10/site-packages/babel/locale-data/pt_PT.dat new file mode 100644 index 000000000..67b1c1350 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/pt_PT.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/pt_ST.dat b/venv/lib/python3.10/site-packages/babel/locale-data/pt_ST.dat new file mode 100644 index 000000000..3883dcbd3 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/pt_ST.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/pt_TL.dat b/venv/lib/python3.10/site-packages/babel/locale-data/pt_TL.dat new file mode 100644 index 000000000..e056a3473 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/pt_TL.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/qu.dat b/venv/lib/python3.10/site-packages/babel/locale-data/qu.dat new file mode 100644 index 000000000..253193ce7 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/qu.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/qu_BO.dat b/venv/lib/python3.10/site-packages/babel/locale-data/qu_BO.dat new file mode 100644 index 000000000..ff0129404 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/qu_BO.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/qu_EC.dat b/venv/lib/python3.10/site-packages/babel/locale-data/qu_EC.dat new file mode 100644 index 000000000..8d9eceee7 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/qu_EC.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/qu_PE.dat b/venv/lib/python3.10/site-packages/babel/locale-data/qu_PE.dat new file mode 100644 index 000000000..4a7a5fd6d Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/qu_PE.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/raj.dat b/venv/lib/python3.10/site-packages/babel/locale-data/raj.dat new file mode 100644 index 000000000..f6cf78694 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/raj.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/raj_IN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/raj_IN.dat new file mode 100644 index 000000000..a61c793f3 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/raj_IN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/rm.dat b/venv/lib/python3.10/site-packages/babel/locale-data/rm.dat new file mode 100644 index 000000000..44db41760 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/rm.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/rm_CH.dat b/venv/lib/python3.10/site-packages/babel/locale-data/rm_CH.dat new file mode 100644 index 000000000..652f61bae Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/rm_CH.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/rn.dat b/venv/lib/python3.10/site-packages/babel/locale-data/rn.dat new file mode 100644 index 000000000..5390c71c6 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/rn.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/rn_BI.dat b/venv/lib/python3.10/site-packages/babel/locale-data/rn_BI.dat new file mode 100644 index 000000000..1845c8a70 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/rn_BI.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ro.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ro.dat new file mode 100644 index 000000000..66d9b0d2f Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ro.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ro_MD.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ro_MD.dat new file mode 100644 index 000000000..4d5d2ea1b Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ro_MD.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ro_RO.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ro_RO.dat new file mode 100644 index 000000000..664fddea9 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ro_RO.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/rof.dat b/venv/lib/python3.10/site-packages/babel/locale-data/rof.dat new file mode 100644 index 000000000..83c2567fb Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/rof.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/rof_TZ.dat b/venv/lib/python3.10/site-packages/babel/locale-data/rof_TZ.dat new file mode 100644 index 000000000..f894d6cf2 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/rof_TZ.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/root.dat b/venv/lib/python3.10/site-packages/babel/locale-data/root.dat new file mode 100644 index 000000000..b75fe9654 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/root.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ru.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ru.dat new file mode 100644 index 000000000..046619f6c Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ru.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ru_BY.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ru_BY.dat new file mode 100644 index 000000000..010680444 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ru_BY.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ru_KG.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ru_KG.dat new file mode 100644 index 000000000..0380b01db Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ru_KG.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ru_KZ.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ru_KZ.dat new file mode 100644 index 000000000..268848460 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ru_KZ.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ru_MD.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ru_MD.dat new file mode 100644 index 000000000..f3b9468b7 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ru_MD.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ru_RU.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ru_RU.dat new file mode 100644 index 000000000..6d5eeb9d0 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ru_RU.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ru_UA.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ru_UA.dat new file mode 100644 index 000000000..9d8ed45a4 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ru_UA.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/rw.dat b/venv/lib/python3.10/site-packages/babel/locale-data/rw.dat new file mode 100644 index 000000000..8cd04342b Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/rw.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/rw_RW.dat b/venv/lib/python3.10/site-packages/babel/locale-data/rw_RW.dat new file mode 100644 index 000000000..62b699d7d Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/rw_RW.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/rwk.dat b/venv/lib/python3.10/site-packages/babel/locale-data/rwk.dat new file mode 100644 index 000000000..a6c34c9ec Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/rwk.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/rwk_TZ.dat b/venv/lib/python3.10/site-packages/babel/locale-data/rwk_TZ.dat new file mode 100644 index 000000000..7f70c585e Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/rwk_TZ.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/sa.dat b/venv/lib/python3.10/site-packages/babel/locale-data/sa.dat new file mode 100644 index 000000000..7ccc4a9fa Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/sa.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/sa_IN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/sa_IN.dat new file mode 100644 index 000000000..9f68e6e59 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/sa_IN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/sah.dat b/venv/lib/python3.10/site-packages/babel/locale-data/sah.dat new file mode 100644 index 000000000..a761b0b0a Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/sah.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/sah_RU.dat b/venv/lib/python3.10/site-packages/babel/locale-data/sah_RU.dat new file mode 100644 index 000000000..2894ddfce Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/sah_RU.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/saq.dat b/venv/lib/python3.10/site-packages/babel/locale-data/saq.dat new file mode 100644 index 000000000..d07a21d28 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/saq.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/saq_KE.dat b/venv/lib/python3.10/site-packages/babel/locale-data/saq_KE.dat new file mode 100644 index 000000000..b71e743ed Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/saq_KE.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/sat.dat b/venv/lib/python3.10/site-packages/babel/locale-data/sat.dat new file mode 100644 index 000000000..b738e5c2f Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/sat.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/sat_Olck.dat b/venv/lib/python3.10/site-packages/babel/locale-data/sat_Olck.dat new file mode 100644 index 000000000..1bdd8fc7d Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/sat_Olck.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/sat_Olck_IN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/sat_Olck_IN.dat new file mode 100644 index 000000000..c9264f773 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/sat_Olck_IN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/sbp.dat b/venv/lib/python3.10/site-packages/babel/locale-data/sbp.dat new file mode 100644 index 000000000..057217271 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/sbp.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/sbp_TZ.dat b/venv/lib/python3.10/site-packages/babel/locale-data/sbp_TZ.dat new file mode 100644 index 000000000..253c0448e Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/sbp_TZ.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/sc.dat b/venv/lib/python3.10/site-packages/babel/locale-data/sc.dat new file mode 100644 index 000000000..1ce7d3aef Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/sc.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/sc_IT.dat b/venv/lib/python3.10/site-packages/babel/locale-data/sc_IT.dat new file mode 100644 index 000000000..48d7c140d Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/sc_IT.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/sd.dat b/venv/lib/python3.10/site-packages/babel/locale-data/sd.dat new file mode 100644 index 000000000..075b476a7 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/sd.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/sd_Arab.dat b/venv/lib/python3.10/site-packages/babel/locale-data/sd_Arab.dat new file mode 100644 index 000000000..05bef300e Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/sd_Arab.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/sd_Arab_PK.dat b/venv/lib/python3.10/site-packages/babel/locale-data/sd_Arab_PK.dat new file mode 100644 index 000000000..0ae2a138b Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/sd_Arab_PK.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/sd_Deva.dat b/venv/lib/python3.10/site-packages/babel/locale-data/sd_Deva.dat new file mode 100644 index 000000000..552faa34e Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/sd_Deva.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/sd_Deva_IN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/sd_Deva_IN.dat new file mode 100644 index 000000000..c758cd276 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/sd_Deva_IN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/se.dat b/venv/lib/python3.10/site-packages/babel/locale-data/se.dat new file mode 100644 index 000000000..8633eb307 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/se.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/se_FI.dat b/venv/lib/python3.10/site-packages/babel/locale-data/se_FI.dat new file mode 100644 index 000000000..f04b348a1 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/se_FI.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/se_NO.dat b/venv/lib/python3.10/site-packages/babel/locale-data/se_NO.dat new file mode 100644 index 000000000..b230d1f29 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/se_NO.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/se_SE.dat b/venv/lib/python3.10/site-packages/babel/locale-data/se_SE.dat new file mode 100644 index 000000000..c0732f27f Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/se_SE.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/seh.dat b/venv/lib/python3.10/site-packages/babel/locale-data/seh.dat new file mode 100644 index 000000000..5bca2b9c3 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/seh.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/seh_MZ.dat b/venv/lib/python3.10/site-packages/babel/locale-data/seh_MZ.dat new file mode 100644 index 000000000..5dea7a173 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/seh_MZ.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ses.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ses.dat new file mode 100644 index 000000000..1cc12c572 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ses.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ses_ML.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ses_ML.dat new file mode 100644 index 000000000..e85fdc10a Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ses_ML.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/sg.dat b/venv/lib/python3.10/site-packages/babel/locale-data/sg.dat new file mode 100644 index 000000000..ae69dcd9a Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/sg.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/sg_CF.dat b/venv/lib/python3.10/site-packages/babel/locale-data/sg_CF.dat new file mode 100644 index 000000000..41feacfc1 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/sg_CF.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/shi.dat b/venv/lib/python3.10/site-packages/babel/locale-data/shi.dat new file mode 100644 index 000000000..0b2de8a1c Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/shi.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/shi_Latn.dat b/venv/lib/python3.10/site-packages/babel/locale-data/shi_Latn.dat new file mode 100644 index 000000000..5afc9f4d8 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/shi_Latn.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/shi_Latn_MA.dat b/venv/lib/python3.10/site-packages/babel/locale-data/shi_Latn_MA.dat new file mode 100644 index 000000000..1fbf4174b Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/shi_Latn_MA.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/shi_Tfng.dat b/venv/lib/python3.10/site-packages/babel/locale-data/shi_Tfng.dat new file mode 100644 index 000000000..782031e43 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/shi_Tfng.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/shi_Tfng_MA.dat b/venv/lib/python3.10/site-packages/babel/locale-data/shi_Tfng_MA.dat new file mode 100644 index 000000000..1fbf4174b Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/shi_Tfng_MA.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/si.dat b/venv/lib/python3.10/site-packages/babel/locale-data/si.dat new file mode 100644 index 000000000..081de595e Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/si.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/si_LK.dat b/venv/lib/python3.10/site-packages/babel/locale-data/si_LK.dat new file mode 100644 index 000000000..c1ee1a1f7 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/si_LK.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/sk.dat b/venv/lib/python3.10/site-packages/babel/locale-data/sk.dat new file mode 100644 index 000000000..e46a3ea1a Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/sk.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/sk_SK.dat b/venv/lib/python3.10/site-packages/babel/locale-data/sk_SK.dat new file mode 100644 index 000000000..7a81b1dc1 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/sk_SK.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/sl.dat b/venv/lib/python3.10/site-packages/babel/locale-data/sl.dat new file mode 100644 index 000000000..1f33352f6 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/sl.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/sl_SI.dat b/venv/lib/python3.10/site-packages/babel/locale-data/sl_SI.dat new file mode 100644 index 000000000..50782c789 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/sl_SI.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/smn.dat b/venv/lib/python3.10/site-packages/babel/locale-data/smn.dat new file mode 100644 index 000000000..54f434c2f Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/smn.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/smn_FI.dat b/venv/lib/python3.10/site-packages/babel/locale-data/smn_FI.dat new file mode 100644 index 000000000..aa8c28612 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/smn_FI.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/sms.dat b/venv/lib/python3.10/site-packages/babel/locale-data/sms.dat new file mode 100644 index 000000000..f7e96545b Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/sms.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/sms_FI.dat b/venv/lib/python3.10/site-packages/babel/locale-data/sms_FI.dat new file mode 100644 index 000000000..d86abf4e8 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/sms_FI.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/sn.dat b/venv/lib/python3.10/site-packages/babel/locale-data/sn.dat new file mode 100644 index 000000000..4a47970be Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/sn.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/sn_ZW.dat b/venv/lib/python3.10/site-packages/babel/locale-data/sn_ZW.dat new file mode 100644 index 000000000..e6f8850f2 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/sn_ZW.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/so.dat b/venv/lib/python3.10/site-packages/babel/locale-data/so.dat new file mode 100644 index 000000000..dec9b5352 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/so.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/so_DJ.dat b/venv/lib/python3.10/site-packages/babel/locale-data/so_DJ.dat new file mode 100644 index 000000000..e26b1feed Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/so_DJ.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/so_ET.dat b/venv/lib/python3.10/site-packages/babel/locale-data/so_ET.dat new file mode 100644 index 000000000..6b0ef75e9 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/so_ET.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/so_KE.dat b/venv/lib/python3.10/site-packages/babel/locale-data/so_KE.dat new file mode 100644 index 000000000..39837e4d4 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/so_KE.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/so_SO.dat b/venv/lib/python3.10/site-packages/babel/locale-data/so_SO.dat new file mode 100644 index 000000000..35362533b Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/so_SO.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/sq.dat b/venv/lib/python3.10/site-packages/babel/locale-data/sq.dat new file mode 100644 index 000000000..e40d66516 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/sq.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/sq_AL.dat b/venv/lib/python3.10/site-packages/babel/locale-data/sq_AL.dat new file mode 100644 index 000000000..b376402ef Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/sq_AL.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/sq_MK.dat b/venv/lib/python3.10/site-packages/babel/locale-data/sq_MK.dat new file mode 100644 index 000000000..d149fec7a Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/sq_MK.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/sq_XK.dat b/venv/lib/python3.10/site-packages/babel/locale-data/sq_XK.dat new file mode 100644 index 000000000..6a8acade5 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/sq_XK.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/sr.dat b/venv/lib/python3.10/site-packages/babel/locale-data/sr.dat new file mode 100644 index 000000000..443a25953 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/sr.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/sr_Cyrl.dat b/venv/lib/python3.10/site-packages/babel/locale-data/sr_Cyrl.dat new file mode 100644 index 000000000..c6f8e4fb4 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/sr_Cyrl.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/sr_Cyrl_BA.dat b/venv/lib/python3.10/site-packages/babel/locale-data/sr_Cyrl_BA.dat new file mode 100644 index 000000000..5adaf7615 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/sr_Cyrl_BA.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/sr_Cyrl_ME.dat b/venv/lib/python3.10/site-packages/babel/locale-data/sr_Cyrl_ME.dat new file mode 100644 index 000000000..92f2b6654 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/sr_Cyrl_ME.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/sr_Cyrl_RS.dat b/venv/lib/python3.10/site-packages/babel/locale-data/sr_Cyrl_RS.dat new file mode 100644 index 000000000..ed764e2e4 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/sr_Cyrl_RS.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/sr_Cyrl_XK.dat b/venv/lib/python3.10/site-packages/babel/locale-data/sr_Cyrl_XK.dat new file mode 100644 index 000000000..c61d21916 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/sr_Cyrl_XK.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/sr_Latn.dat b/venv/lib/python3.10/site-packages/babel/locale-data/sr_Latn.dat new file mode 100644 index 000000000..509c83327 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/sr_Latn.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/sr_Latn_BA.dat b/venv/lib/python3.10/site-packages/babel/locale-data/sr_Latn_BA.dat new file mode 100644 index 000000000..9a6d82552 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/sr_Latn_BA.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/sr_Latn_ME.dat b/venv/lib/python3.10/site-packages/babel/locale-data/sr_Latn_ME.dat new file mode 100644 index 000000000..19c9e5461 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/sr_Latn_ME.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/sr_Latn_RS.dat b/venv/lib/python3.10/site-packages/babel/locale-data/sr_Latn_RS.dat new file mode 100644 index 000000000..ed764e2e4 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/sr_Latn_RS.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/sr_Latn_XK.dat b/venv/lib/python3.10/site-packages/babel/locale-data/sr_Latn_XK.dat new file mode 100644 index 000000000..b59c39034 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/sr_Latn_XK.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/su.dat b/venv/lib/python3.10/site-packages/babel/locale-data/su.dat new file mode 100644 index 000000000..f69777f72 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/su.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/su_Latn.dat b/venv/lib/python3.10/site-packages/babel/locale-data/su_Latn.dat new file mode 100644 index 000000000..6018dc6f8 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/su_Latn.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/su_Latn_ID.dat b/venv/lib/python3.10/site-packages/babel/locale-data/su_Latn_ID.dat new file mode 100644 index 000000000..53d733db3 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/su_Latn_ID.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/sv.dat b/venv/lib/python3.10/site-packages/babel/locale-data/sv.dat new file mode 100644 index 000000000..ebe249594 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/sv.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/sv_AX.dat b/venv/lib/python3.10/site-packages/babel/locale-data/sv_AX.dat new file mode 100644 index 000000000..b5a77787d Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/sv_AX.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/sv_FI.dat b/venv/lib/python3.10/site-packages/babel/locale-data/sv_FI.dat new file mode 100644 index 000000000..6cdf35dfe Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/sv_FI.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/sv_SE.dat b/venv/lib/python3.10/site-packages/babel/locale-data/sv_SE.dat new file mode 100644 index 000000000..fba24ad8b Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/sv_SE.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/sw.dat b/venv/lib/python3.10/site-packages/babel/locale-data/sw.dat new file mode 100644 index 000000000..683bcaad6 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/sw.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/sw_CD.dat b/venv/lib/python3.10/site-packages/babel/locale-data/sw_CD.dat new file mode 100644 index 000000000..c54a891bd Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/sw_CD.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/sw_KE.dat b/venv/lib/python3.10/site-packages/babel/locale-data/sw_KE.dat new file mode 100644 index 000000000..6bd77d91b Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/sw_KE.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/sw_TZ.dat b/venv/lib/python3.10/site-packages/babel/locale-data/sw_TZ.dat new file mode 100644 index 000000000..848ecb305 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/sw_TZ.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/sw_UG.dat b/venv/lib/python3.10/site-packages/babel/locale-data/sw_UG.dat new file mode 100644 index 000000000..cfe9891fe Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/sw_UG.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ta.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ta.dat new file mode 100644 index 000000000..a510611d8 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ta.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ta_IN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ta_IN.dat new file mode 100644 index 000000000..b0bbe4a8f Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ta_IN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ta_LK.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ta_LK.dat new file mode 100644 index 000000000..3dca7dcff Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ta_LK.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ta_MY.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ta_MY.dat new file mode 100644 index 000000000..05bb3212d Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ta_MY.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ta_SG.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ta_SG.dat new file mode 100644 index 000000000..d79c767da Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ta_SG.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/te.dat b/venv/lib/python3.10/site-packages/babel/locale-data/te.dat new file mode 100644 index 000000000..6d409f6a3 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/te.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/te_IN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/te_IN.dat new file mode 100644 index 000000000..47184a7f8 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/te_IN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/teo.dat b/venv/lib/python3.10/site-packages/babel/locale-data/teo.dat new file mode 100644 index 000000000..bcbb32058 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/teo.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/teo_KE.dat b/venv/lib/python3.10/site-packages/babel/locale-data/teo_KE.dat new file mode 100644 index 000000000..3b00c8e5d Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/teo_KE.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/teo_UG.dat b/venv/lib/python3.10/site-packages/babel/locale-data/teo_UG.dat new file mode 100644 index 000000000..7a0a7415a Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/teo_UG.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/tg.dat b/venv/lib/python3.10/site-packages/babel/locale-data/tg.dat new file mode 100644 index 000000000..e42028dae Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/tg.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/tg_TJ.dat b/venv/lib/python3.10/site-packages/babel/locale-data/tg_TJ.dat new file mode 100644 index 000000000..7cf63ac2f Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/tg_TJ.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/th.dat b/venv/lib/python3.10/site-packages/babel/locale-data/th.dat new file mode 100644 index 000000000..389787279 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/th.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/th_TH.dat b/venv/lib/python3.10/site-packages/babel/locale-data/th_TH.dat new file mode 100644 index 000000000..c5d4f40bd Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/th_TH.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ti.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ti.dat new file mode 100644 index 000000000..bd0b6d38e Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ti.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ti_ER.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ti_ER.dat new file mode 100644 index 000000000..032e3059c Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ti_ER.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ti_ET.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ti_ET.dat new file mode 100644 index 000000000..4a8f9688f Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ti_ET.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/tk.dat b/venv/lib/python3.10/site-packages/babel/locale-data/tk.dat new file mode 100644 index 000000000..ae58801f2 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/tk.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/tk_TM.dat b/venv/lib/python3.10/site-packages/babel/locale-data/tk_TM.dat new file mode 100644 index 000000000..523cbe26f Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/tk_TM.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/to.dat b/venv/lib/python3.10/site-packages/babel/locale-data/to.dat new file mode 100644 index 000000000..c02e73e9e Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/to.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/to_TO.dat b/venv/lib/python3.10/site-packages/babel/locale-data/to_TO.dat new file mode 100644 index 000000000..3309642a1 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/to_TO.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/tok.dat b/venv/lib/python3.10/site-packages/babel/locale-data/tok.dat new file mode 100644 index 000000000..e21db3313 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/tok.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/tok_001.dat b/venv/lib/python3.10/site-packages/babel/locale-data/tok_001.dat new file mode 100644 index 000000000..1896e1e1b Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/tok_001.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/tr.dat b/venv/lib/python3.10/site-packages/babel/locale-data/tr.dat new file mode 100644 index 000000000..2ea265068 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/tr.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/tr_CY.dat b/venv/lib/python3.10/site-packages/babel/locale-data/tr_CY.dat new file mode 100644 index 000000000..02fbd0584 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/tr_CY.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/tr_TR.dat b/venv/lib/python3.10/site-packages/babel/locale-data/tr_TR.dat new file mode 100644 index 000000000..a2f6c6db2 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/tr_TR.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/tt.dat b/venv/lib/python3.10/site-packages/babel/locale-data/tt.dat new file mode 100644 index 000000000..290b22374 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/tt.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/tt_RU.dat b/venv/lib/python3.10/site-packages/babel/locale-data/tt_RU.dat new file mode 100644 index 000000000..0c25192f1 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/tt_RU.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/twq.dat b/venv/lib/python3.10/site-packages/babel/locale-data/twq.dat new file mode 100644 index 000000000..91ce40a12 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/twq.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/twq_NE.dat b/venv/lib/python3.10/site-packages/babel/locale-data/twq_NE.dat new file mode 100644 index 000000000..dbf625660 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/twq_NE.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/tzm.dat b/venv/lib/python3.10/site-packages/babel/locale-data/tzm.dat new file mode 100644 index 000000000..576838c1b Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/tzm.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/tzm_MA.dat b/venv/lib/python3.10/site-packages/babel/locale-data/tzm_MA.dat new file mode 100644 index 000000000..a025e509c Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/tzm_MA.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ug.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ug.dat new file mode 100644 index 000000000..9851a502b Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ug.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ug_CN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ug_CN.dat new file mode 100644 index 000000000..8e2a95452 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ug_CN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/uk.dat b/venv/lib/python3.10/site-packages/babel/locale-data/uk.dat new file mode 100644 index 000000000..03f825dae Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/uk.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/uk_UA.dat b/venv/lib/python3.10/site-packages/babel/locale-data/uk_UA.dat new file mode 100644 index 000000000..8a011dc2c Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/uk_UA.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ur.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ur.dat new file mode 100644 index 000000000..3c8fadac2 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ur.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ur_IN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ur_IN.dat new file mode 100644 index 000000000..f9b9bd71b Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ur_IN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/ur_PK.dat b/venv/lib/python3.10/site-packages/babel/locale-data/ur_PK.dat new file mode 100644 index 000000000..bdfae5cfe Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/ur_PK.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/uz.dat b/venv/lib/python3.10/site-packages/babel/locale-data/uz.dat new file mode 100644 index 000000000..d69801ebe Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/uz.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/uz_Arab.dat b/venv/lib/python3.10/site-packages/babel/locale-data/uz_Arab.dat new file mode 100644 index 000000000..1d0ed4257 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/uz_Arab.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/uz_Arab_AF.dat b/venv/lib/python3.10/site-packages/babel/locale-data/uz_Arab_AF.dat new file mode 100644 index 000000000..4c08cf02d Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/uz_Arab_AF.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/uz_Cyrl.dat b/venv/lib/python3.10/site-packages/babel/locale-data/uz_Cyrl.dat new file mode 100644 index 000000000..048f18acd Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/uz_Cyrl.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/uz_Cyrl_UZ.dat b/venv/lib/python3.10/site-packages/babel/locale-data/uz_Cyrl_UZ.dat new file mode 100644 index 000000000..fa25c5360 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/uz_Cyrl_UZ.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/uz_Latn.dat b/venv/lib/python3.10/site-packages/babel/locale-data/uz_Latn.dat new file mode 100644 index 000000000..47a403314 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/uz_Latn.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/uz_Latn_UZ.dat b/venv/lib/python3.10/site-packages/babel/locale-data/uz_Latn_UZ.dat new file mode 100644 index 000000000..fa25c5360 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/uz_Latn_UZ.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/vai.dat b/venv/lib/python3.10/site-packages/babel/locale-data/vai.dat new file mode 100644 index 000000000..002c64698 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/vai.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/vai_Latn.dat b/venv/lib/python3.10/site-packages/babel/locale-data/vai_Latn.dat new file mode 100644 index 000000000..9a32b9118 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/vai_Latn.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/vai_Latn_LR.dat b/venv/lib/python3.10/site-packages/babel/locale-data/vai_Latn_LR.dat new file mode 100644 index 000000000..dc7489ce3 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/vai_Latn_LR.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/vai_Vaii.dat b/venv/lib/python3.10/site-packages/babel/locale-data/vai_Vaii.dat new file mode 100644 index 000000000..331a1f037 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/vai_Vaii.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/vai_Vaii_LR.dat b/venv/lib/python3.10/site-packages/babel/locale-data/vai_Vaii_LR.dat new file mode 100644 index 000000000..dc7489ce3 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/vai_Vaii_LR.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/vi.dat b/venv/lib/python3.10/site-packages/babel/locale-data/vi.dat new file mode 100644 index 000000000..a84cdff5a Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/vi.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/vi_VN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/vi_VN.dat new file mode 100644 index 000000000..c8accbdfc Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/vi_VN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/vun.dat b/venv/lib/python3.10/site-packages/babel/locale-data/vun.dat new file mode 100644 index 000000000..c5734b731 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/vun.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/vun_TZ.dat b/venv/lib/python3.10/site-packages/babel/locale-data/vun_TZ.dat new file mode 100644 index 000000000..4988c1786 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/vun_TZ.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/wae.dat b/venv/lib/python3.10/site-packages/babel/locale-data/wae.dat new file mode 100644 index 000000000..bc9256b08 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/wae.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/wae_CH.dat b/venv/lib/python3.10/site-packages/babel/locale-data/wae_CH.dat new file mode 100644 index 000000000..99e337ca6 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/wae_CH.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/wo.dat b/venv/lib/python3.10/site-packages/babel/locale-data/wo.dat new file mode 100644 index 000000000..e6397a35d Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/wo.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/wo_SN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/wo_SN.dat new file mode 100644 index 000000000..5a51e8f18 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/wo_SN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/xh.dat b/venv/lib/python3.10/site-packages/babel/locale-data/xh.dat new file mode 100644 index 000000000..2a5895e75 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/xh.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/xh_ZA.dat b/venv/lib/python3.10/site-packages/babel/locale-data/xh_ZA.dat new file mode 100644 index 000000000..7bcbf8b99 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/xh_ZA.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/xog.dat b/venv/lib/python3.10/site-packages/babel/locale-data/xog.dat new file mode 100644 index 000000000..4fc3e7910 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/xog.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/xog_UG.dat b/venv/lib/python3.10/site-packages/babel/locale-data/xog_UG.dat new file mode 100644 index 000000000..2c2f265d5 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/xog_UG.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/yav.dat b/venv/lib/python3.10/site-packages/babel/locale-data/yav.dat new file mode 100644 index 000000000..e03e284e1 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/yav.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/yav_CM.dat b/venv/lib/python3.10/site-packages/babel/locale-data/yav_CM.dat new file mode 100644 index 000000000..28aa4031f Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/yav_CM.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/yi.dat b/venv/lib/python3.10/site-packages/babel/locale-data/yi.dat new file mode 100644 index 000000000..2fa95c065 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/yi.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/yi_001.dat b/venv/lib/python3.10/site-packages/babel/locale-data/yi_001.dat new file mode 100644 index 000000000..09fb4cc30 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/yi_001.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/yo.dat b/venv/lib/python3.10/site-packages/babel/locale-data/yo.dat new file mode 100644 index 000000000..a1941ce4a Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/yo.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/yo_BJ.dat b/venv/lib/python3.10/site-packages/babel/locale-data/yo_BJ.dat new file mode 100644 index 000000000..ccf0f8c7e Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/yo_BJ.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/yo_NG.dat b/venv/lib/python3.10/site-packages/babel/locale-data/yo_NG.dat new file mode 100644 index 000000000..fc8462725 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/yo_NG.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/yrl.dat b/venv/lib/python3.10/site-packages/babel/locale-data/yrl.dat new file mode 100644 index 000000000..d9ce584a5 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/yrl.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/yrl_BR.dat b/venv/lib/python3.10/site-packages/babel/locale-data/yrl_BR.dat new file mode 100644 index 000000000..af371979b Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/yrl_BR.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/yrl_CO.dat b/venv/lib/python3.10/site-packages/babel/locale-data/yrl_CO.dat new file mode 100644 index 000000000..ae625e17e Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/yrl_CO.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/yrl_VE.dat b/venv/lib/python3.10/site-packages/babel/locale-data/yrl_VE.dat new file mode 100644 index 000000000..03da2a831 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/yrl_VE.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/yue.dat b/venv/lib/python3.10/site-packages/babel/locale-data/yue.dat new file mode 100644 index 000000000..0072661ba Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/yue.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/yue_Hans.dat b/venv/lib/python3.10/site-packages/babel/locale-data/yue_Hans.dat new file mode 100644 index 000000000..794fa47d2 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/yue_Hans.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/yue_Hans_CN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/yue_Hans_CN.dat new file mode 100644 index 000000000..d9be5919c Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/yue_Hans_CN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/yue_Hant.dat b/venv/lib/python3.10/site-packages/babel/locale-data/yue_Hant.dat new file mode 100644 index 000000000..a6f177a6c Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/yue_Hant.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/yue_Hant_HK.dat b/venv/lib/python3.10/site-packages/babel/locale-data/yue_Hant_HK.dat new file mode 100644 index 000000000..88f0913e5 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/yue_Hant_HK.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/zgh.dat b/venv/lib/python3.10/site-packages/babel/locale-data/zgh.dat new file mode 100644 index 000000000..1db930023 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/zgh.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/zgh_MA.dat b/venv/lib/python3.10/site-packages/babel/locale-data/zgh_MA.dat new file mode 100644 index 000000000..6acec163f Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/zgh_MA.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/zh.dat b/venv/lib/python3.10/site-packages/babel/locale-data/zh.dat new file mode 100644 index 000000000..eac1f56fe Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/zh.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/zh_Hans.dat b/venv/lib/python3.10/site-packages/babel/locale-data/zh_Hans.dat new file mode 100644 index 000000000..cb30aaf59 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/zh_Hans.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/zh_Hans_CN.dat b/venv/lib/python3.10/site-packages/babel/locale-data/zh_Hans_CN.dat new file mode 100644 index 000000000..ef58a4dfe Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/zh_Hans_CN.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/zh_Hans_HK.dat b/venv/lib/python3.10/site-packages/babel/locale-data/zh_Hans_HK.dat new file mode 100644 index 000000000..9aa8ae341 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/zh_Hans_HK.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/zh_Hans_MO.dat b/venv/lib/python3.10/site-packages/babel/locale-data/zh_Hans_MO.dat new file mode 100644 index 000000000..5f61ef533 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/zh_Hans_MO.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/zh_Hans_SG.dat b/venv/lib/python3.10/site-packages/babel/locale-data/zh_Hans_SG.dat new file mode 100644 index 000000000..987407ba9 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/zh_Hans_SG.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/zh_Hant.dat b/venv/lib/python3.10/site-packages/babel/locale-data/zh_Hant.dat new file mode 100644 index 000000000..92be0e4a7 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/zh_Hant.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/zh_Hant_HK.dat b/venv/lib/python3.10/site-packages/babel/locale-data/zh_Hant_HK.dat new file mode 100644 index 000000000..6c40ef10e Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/zh_Hant_HK.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/zh_Hant_MO.dat b/venv/lib/python3.10/site-packages/babel/locale-data/zh_Hant_MO.dat new file mode 100644 index 000000000..305cbe56c Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/zh_Hant_MO.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/zh_Hant_TW.dat b/venv/lib/python3.10/site-packages/babel/locale-data/zh_Hant_TW.dat new file mode 100644 index 000000000..62221bcd1 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/zh_Hant_TW.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/zu.dat b/venv/lib/python3.10/site-packages/babel/locale-data/zu.dat new file mode 100644 index 000000000..76bd5dcb6 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/zu.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/locale-data/zu_ZA.dat b/venv/lib/python3.10/site-packages/babel/locale-data/zu_ZA.dat new file mode 100644 index 000000000..35cff0a44 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/locale-data/zu_ZA.dat differ diff --git a/venv/lib/python3.10/site-packages/babel/localedata.py b/venv/lib/python3.10/site-packages/babel/localedata.py new file mode 100644 index 000000000..f1e8a1291 --- /dev/null +++ b/venv/lib/python3.10/site-packages/babel/localedata.py @@ -0,0 +1,254 @@ +""" + 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-2023 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 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: + 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.10/site-packages/babel/localtime/__init__.py b/venv/lib/python3.10/site-packages/babel/localtime/__init__.py new file mode 100644 index 000000000..e1ece7342 --- /dev/null +++ b/venv/lib/python3.10/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-2023 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.10/site-packages/babel/localtime/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/babel/localtime/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 000000000..8f91a55e7 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/localtime/__pycache__/__init__.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/babel/localtime/__pycache__/_fallback.cpython-310.pyc b/venv/lib/python3.10/site-packages/babel/localtime/__pycache__/_fallback.cpython-310.pyc new file mode 100644 index 000000000..1a6410d00 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/localtime/__pycache__/_fallback.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/babel/localtime/__pycache__/_helpers.cpython-310.pyc b/venv/lib/python3.10/site-packages/babel/localtime/__pycache__/_helpers.cpython-310.pyc new file mode 100644 index 000000000..f93b984d4 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/localtime/__pycache__/_helpers.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/babel/localtime/__pycache__/_unix.cpython-310.pyc b/venv/lib/python3.10/site-packages/babel/localtime/__pycache__/_unix.cpython-310.pyc new file mode 100644 index 000000000..03dbb9a17 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/localtime/__pycache__/_unix.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/babel/localtime/__pycache__/_win32.cpython-310.pyc b/venv/lib/python3.10/site-packages/babel/localtime/__pycache__/_win32.cpython-310.pyc new file mode 100644 index 000000000..00804fa1f Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/localtime/__pycache__/_win32.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/babel/localtime/_fallback.py b/venv/lib/python3.10/site-packages/babel/localtime/_fallback.py new file mode 100644 index 000000000..14979a53b --- /dev/null +++ b/venv/lib/python3.10/site-packages/babel/localtime/_fallback.py @@ -0,0 +1,44 @@ +""" + babel.localtime._fallback + ~~~~~~~~~~~~~~~~~~~~~~~~~ + + Emulated fallback local timezone when all else fails. + + :copyright: (c) 2013-2023 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.10/site-packages/babel/localtime/_helpers.py b/venv/lib/python3.10/site-packages/babel/localtime/_helpers.py new file mode 100644 index 000000000..159f9a569 --- /dev/null +++ b/venv/lib/python3.10/site-packages/babel/localtime/_helpers.py @@ -0,0 +1,43 @@ +try: + import pytz +except ModuleNotFoundError: + pytz = None + import zoneinfo + + +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 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.10/site-packages/babel/localtime/_unix.py b/venv/lib/python3.10/site-packages/babel/localtime/_unix.py new file mode 100644 index 000000000..eb81beb61 --- /dev/null +++ b/venv/lib/python3.10/site-packages/babel/localtime/_unix.py @@ -0,0 +1,98 @@ +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: + zone_name = link_dst[pos + 10:] + 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.10/site-packages/babel/localtime/_win32.py b/venv/lib/python3.10/site-packages/babel/localtime/_win32.py new file mode 100644 index 000000000..1a52567bc --- /dev/null +++ b/venv/lib/python3.10/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.10/site-packages/babel/messages/__init__.py b/venv/lib/python3.10/site-packages/babel/messages/__init__.py new file mode 100644 index 000000000..883a2e055 --- /dev/null +++ b/venv/lib/python3.10/site-packages/babel/messages/__init__.py @@ -0,0 +1,21 @@ +""" + babel.messages + ~~~~~~~~~~~~~~ + + Support for ``gettext`` message catalogs. + + :copyright: (c) 2013-2023 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.10/site-packages/babel/messages/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/babel/messages/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 000000000..7a9421416 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/messages/__pycache__/__init__.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/babel/messages/__pycache__/catalog.cpython-310.pyc b/venv/lib/python3.10/site-packages/babel/messages/__pycache__/catalog.cpython-310.pyc new file mode 100644 index 000000000..376246a5a Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/messages/__pycache__/catalog.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/babel/messages/__pycache__/checkers.cpython-310.pyc b/venv/lib/python3.10/site-packages/babel/messages/__pycache__/checkers.cpython-310.pyc new file mode 100644 index 000000000..2466b7646 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/messages/__pycache__/checkers.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/babel/messages/__pycache__/extract.cpython-310.pyc b/venv/lib/python3.10/site-packages/babel/messages/__pycache__/extract.cpython-310.pyc new file mode 100644 index 000000000..f22e7c2b0 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/messages/__pycache__/extract.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/babel/messages/__pycache__/frontend.cpython-310.pyc b/venv/lib/python3.10/site-packages/babel/messages/__pycache__/frontend.cpython-310.pyc new file mode 100644 index 000000000..71c048f10 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/messages/__pycache__/frontend.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/babel/messages/__pycache__/jslexer.cpython-310.pyc b/venv/lib/python3.10/site-packages/babel/messages/__pycache__/jslexer.cpython-310.pyc new file mode 100644 index 000000000..33aef831a Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/messages/__pycache__/jslexer.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/babel/messages/__pycache__/mofile.cpython-310.pyc b/venv/lib/python3.10/site-packages/babel/messages/__pycache__/mofile.cpython-310.pyc new file mode 100644 index 000000000..ae902414e Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/messages/__pycache__/mofile.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/babel/messages/__pycache__/plurals.cpython-310.pyc b/venv/lib/python3.10/site-packages/babel/messages/__pycache__/plurals.cpython-310.pyc new file mode 100644 index 000000000..d29d55d49 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/messages/__pycache__/plurals.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/babel/messages/__pycache__/pofile.cpython-310.pyc b/venv/lib/python3.10/site-packages/babel/messages/__pycache__/pofile.cpython-310.pyc new file mode 100644 index 000000000..bc63a5e15 Binary files /dev/null and b/venv/lib/python3.10/site-packages/babel/messages/__pycache__/pofile.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/babel/messages/catalog.py b/venv/lib/python3.10/site-packages/babel/messages/catalog.py new file mode 100644 index 000000000..20a3166e4 --- /dev/null +++ b/venv/lib/python3.10/site-packages/babel/messages/catalog.py @@ -0,0 +1,941 @@ +""" + babel.messages.catalog + ~~~~~~~~~~~~~~~~~~~~~~ + + Data structures for message catalogs. + + :copyright: (c) 2013-2023 by the Babel Team. + :license: BSD, see LICENSE for more details. +""" +from __future__ import annotations + +import datetime +import re +from collections import OrderedDict +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 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__ = ['Message', 'Catalog', '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 _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') + 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) + + +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()) + + +class Catalog: + """Representation of a message catalog.""" + + def __init__( + self, + locale: str | Locale | 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: OrderedDict[str | tuple[str, str], Message] = OrderedDict() + + 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: OrderedDict[str | tuple[str, str], Message] = OrderedDict() + 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]]: + headers: list[tuple[str, str]] = [] + headers.append(("Project-Id-Version", f"{self.project} {self.version}")) + headers.append(('Report-Msgid-Bugs-To', self.msgid_bugs_address)) + headers.append(('POT-Creation-Date', + format_datetime(self.creation_date, 'yyyy-MM-dd HH:mmZ', + locale='en'))) + if isinstance(self.revision_date, (datetime.datetime, datetime.time, int, float)): + headers.append(('PO-Revision-Date', + format_datetime(self.revision_date, + 'yyyy-MM-dd HH:mmZ', locale='en'))) + else: + headers.append(('PO-Revision-Date', self.revision_date)) + headers.append(('Last-Translator', self.last_translator)) + if self.locale_identifier: + headers.append(('Language', str(self.locale_identifier))) + if self.locale_identifier and ('LANGUAGE' in self.language_team): + headers.append(('Language-Team', + self.language_team.replace('LANGUAGE', + str(self.locale_identifier)))) + else: + headers.append(('Language-Team', self.language_team)) + if self.locale is not None: + headers.append(('Plural-Forms', self.plural_forms)) + headers.append(('MIME-Version', '1.0')) + headers.append(("Content-Type", f"text/plain; charset={self.charset}")) + headers.append(('Content-Transfer-Encoding', '8bit')) + headers.append(("Generated-By", f"Babel {VERSION}\n")) + return headers + + def _force_text(self, 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) + + def _set_mime_headers(self, headers: Iterable[tuple[str, str]]) -> None: + for name, value in headers: + name = self._force_text(name.lower(), encoding=self.charset) + value = self._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('-', '_') + self._set_locale(value) + 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 + message = current + 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, + ) -> 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 + """ + messages = self._messages + remaining = messages.copy() + self._messages = OrderedDict() + + # Prepare for fuzzy matching + fuzzy_candidates = [] + if not no_fuzzy_matching: + fuzzy_candidates = {} + for msgid in messages: + if msgid and messages[msgid].string: + key = self._key_for(msgid) + ctxt = messages[msgid].context + modified_key = key.lower().strip() + fuzzy_candidates[modified_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 + if isinstance(key, tuple): + matchkey = key[0] # just the msgid, no context + else: + matchkey = key + matches = get_close_matches(matchkey.lower().strip(), + 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 + self.creation_date = template.creation_date + + 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.10/site-packages/babel/messages/checkers.py b/venv/lib/python3.10/site-packages/babel/messages/checkers.py new file mode 100644 index 000000000..056f3e982 --- /dev/null +++ b/venv/lib/python3.10/site-packages/babel/messages/checkers.py @@ -0,0 +1,173 @@ +""" + babel.messages.checkers + ~~~~~~~~~~~~~~~~~~~~~~~ + + Various routines that help with validation of translations. + + :since: version 0.9 + + :copyright: (c) 2013-2023 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. + + The behavior of this function is undefined if the string does not use + string formatting. + + 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)) + + # 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('incompatible format for ' + 'placeholder %r: ' + '%r and %r are not compatible' % + (name, typechar, type_map[name])) + + +def _find_checkers() -> list[Callable[[Catalog | None, Message], object]]: + checkers: list[Callable[[Catalog | None, Message], object]] = [] + try: + from pkg_resources import working_set + except ImportError: + pass + else: + for entry_point in working_set.iter_entry_points('babel.checkers'): + checkers.append(entry_point.load()) + if len(checkers) == 0: + # if pkg_resources is 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.10/site-packages/babel/messages/extract.py b/venv/lib/python3.10/site-packages/babel/messages/extract.py new file mode 100644 index 000000000..b6dce6fdb --- /dev/null +++ b/venv/lib/python3.10/site-packages/babel/messages/extract.py @@ -0,0 +1,792 @@ +""" + 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-2023 by the Babel Team. + :license: BSD, see LICENSE for more details. +""" +from __future__ import annotations + +import ast +import io +import os +import sys +from collections.abc import ( + Callable, + Collection, + Generator, + Iterable, + Mapping, + MutableSequence, +) +from os.path import relpath +from textwrap import dedent +from tokenize import COMMENT, NAME, OP, STRING, generate_tokens +from typing import TYPE_CHECKING, Any + +from babel.util import parse_encoding, parse_future_flags, pathmatch + +if TYPE_CHECKING: + from typing import IO, Protocol + + from _typeshed import SupportsItems, SupportsRead, SupportsReadline + from typing_extensions import Final, TypeAlias, TypedDict + + 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: ... + + _Keyword: TypeAlias = tuple[int | tuple[int, int] | tuple[int, str], ...] | None + + # 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')] + + +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 + 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 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] + """ + func = 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: + try: + from pkg_resources import working_set + except ImportError: + pass + else: + for entry_point in working_set.iter_entry_points(GROUP_NAME, + method): + func = entry_point.load(require=True) + break + if func is None: + # if pkg_resources is not available or no usable egg-info was found + # (see #230), we resort to looking up the builtin extractors + # directly + builtin = { + 'ignore': extract_nothing, + 'python': extract_python, + 'javascript': extract_javascript + } + func = builtin.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: + spec = keywords[funcname] or (1,) if funcname else (1,) + if not isinstance(messages, (list, tuple)): + messages = [messages] + if not messages: + continue + + # Validate the messages against the keyword's specification + context = None + msgs = [] + invalid = False + # last_index is 1 based like the keyword spec + last_index = len(messages) + for index in spec: + if isinstance(index, tuple): + context = messages[index[0] - 1] + continue + if last_index < index: + # Not enough arguments + invalid = True + break + message = messages[index - 1] + if message is None: + invalid = True + break + msgs.append(message) + if invalid: + continue + + # 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 + if not messages[first_msg_index]: + # An empty string msgid isn't valid, emit a warning + 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" + ) + continue + + messages = tuple(msgs) + if len(messages) == 1: + messages = messages[0] + + if strip_comment_tags: + _strip_comment_tags(comments, comment_tags) + yield lineno, messages, comments, context + + +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) + 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: + message_lineno = lineno + 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: + 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 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 + + +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.Str): + return body.s + if isinstance(body, ast.JoinedStr): # f-string + if all(isinstance(node, ast.Str) for node in body.values): + return ''.join(node.s for node in body.values) + if all(isinstance(node, ast.Constant) for node in body.values): + return ''.join(str(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 diff --git a/venv/lib/python3.10/site-packages/babel/messages/frontend.py b/venv/lib/python3.10/site-packages/babel/messages/frontend.py new file mode 100644 index 000000000..af7e1d11d --- /dev/null +++ b/venv/lib/python3.10/site-packages/babel/messages/frontend.py @@ -0,0 +1,1142 @@ +""" + babel.messages.frontend + ~~~~~~~~~~~~~~~~~~~~~~~ + + Frontends for the message extraction functionality. + + :copyright: (c) 2013-2023 by the Babel Team. + :license: BSD, see LICENSE for more details. +""" + +import datetime +import fnmatch +import logging +import optparse +import os +import re +import shutil +import sys +import tempfile +from collections import OrderedDict +from configparser import RawConfigParser +from io import StringIO +from typing import Iterable + +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') + +try: + # See: https://setuptools.pypa.io/en/latest/deprecated/distutils-legacy.html + from setuptools import Command as _Command + distutils_log = log # "distutils.log → (no replacement yet)" + + try: + from setuptools.errors import BaseError, OptionError, SetupError + except ImportError: # Error aliases only added in setuptools 59 (2021-11). + OptionError = SetupError = BaseError = Exception + +except ImportError: + from distutils import log as distutils_log + from distutils.cmd import Command as _Command + from distutils.errors import DistutilsError as BaseError + from distutils.errors import DistutilsOptionError as OptionError + from distutils.errors import DistutilsSetupError as SetupError + + +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 Command(_Command): + # 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 = distutils_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 + + +class compile_catalog(Command): + """Catalog compilation command for use in ``setup.py`` scripts. + + If correctly installed, this command is available to Setuptools-using + setup scripts automatically. For projects using plain old ``distutils``, + the command needs to be registered explicitly in ``setup.py``:: + + from babel.messages.frontend import compile_catalog + + setup( + ... + cmdclass = {'compile_catalog': compile_catalog} + ) + + .. versionadded:: 0.9 + """ + + 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 extract_messages(Command): + """Message extraction command for use in ``setup.py`` scripts. + + If correctly installed, this command is available to Setuptools-using + setup scripts automatically. For projects using plain old ``distutils``, + the command needs to be registered explicitly in ``setup.py``:: + + from babel.messages.frontend import extract_messages + + setup( + ... + cmdclass = {'extract_messages': extract_messages} + ) + """ + + 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'), + ] + 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',), + } + 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 + + 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(self.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)) + + 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: + with open(self.mapping_file) as fileobj: + method_map, options_map = parse_mapping(fileobj) + 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(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 + + +def check_message_extractors(dist, name, value): + """Validate the ``message_extractors`` keyword argument to ``setup()``. + + :param dist: the distutils/setuptools ``Distribution`` object + :param name: the name of the keyword argument (should always be + "message_extractors") + :param value: the value of the keyword argument + :raise `DistutilsSetupError`: if the value is not valid + """ + assert name == 'message_extractors' + if not isinstance(value, dict): + raise SetupError( + 'the value of the "message_extractors" ' + 'parameter must be a dictionary' + ) + + +class init_catalog(Command): + """New catalog initialization command for use in ``setup.py`` scripts. + + If correctly installed, this command is available to Setuptools-using + setup scripts automatically. For projects using plain old ``distutils``, + the command needs to be registered explicitly in ``setup.py``:: + + from babel.messages.frontend import init_catalog + + setup( + ... + cmdclass = {'init_catalog': init_catalog} + ) + """ + + 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 update_catalog(Command): + """Catalog merging command for use in ``setup.py`` scripts. + + If correctly installed, this command is available to Setuptools-using + setup scripts automatically. For projects using plain old ``distutils``, + the command needs to be registered explicitly in ``setup.py``:: + + from babel.messages.frontend import update_catalog + + setup( + ... + cmdclass = {'update_catalog': update_catalog} + ) + + .. versionadded:: 0.9 + """ + + 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'), + ] + boolean_options = [ + 'omit-header', 'no-wrap', 'ignore-obsolete', 'init-missing', + 'no-fuzzy-matching', 'previous', 'update-header-comment', + 'check', + ] + + 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 + + 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 + ) + + 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': compile_catalog, + 'extract': extract_messages, + 'init': init_catalog, + 'update': update_catalog, + } + + 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, Command) + cmdinst.initialize_options() + + parser = optparse.OptionParser( + usage=self.usage % (cmdname, ''), + description=self.commands[cmdname] + ) + as_args = getattr(cmdclass, "as_args", ()) + 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): + """Parse an extraction method mapping from a file-like object. + + >>> buf = StringIO(''' + ... [extractors] + ... custom = mypackage.module:myfunc + ... + ... # Python source files + ... [python: **.py] + ... + ... # Genshi templates + ... [genshi: **/templates/**.html] + ... include_attrs = + ... [genshi: **/templates/**.txt] + ... template_class = genshi.template:TextTemplate + ... encoding = latin-1 + ... + ... # Some custom extractor + ... [custom: **/custom/*.*] + ... ''') + + >>> method_map, options_map = parse_mapping(buf) + >>> len(method_map) + 4 + + >>> method_map[0] + ('**.py', 'python') + >>> options_map['**.py'] + {} + >>> method_map[1] + ('**/templates/**.html', 'genshi') + >>> options_map['**/templates/**.html']['include_attrs'] + '' + >>> method_map[2] + ('**/templates/**.txt', 'genshi') + >>> options_map['**/templates/**.txt']['template_class'] + 'genshi.template:TextTemplate' + >>> options_map['**/templates/**.txt']['encoding'] + 'latin-1' + + >>> method_map[3] + ('**/custom/*.*', 'mypackage.module:myfunc') + >>> options_map['**/custom/*.*'] + {} + + :param fileobj: a readable file-like object containing the configuration + text to parse + :see: `extract_from_directory` + """ + extractors = {} + method_map = [] + options_map = {} + + parser = RawConfigParser() + parser._sections = OrderedDict(parser._sections) # We need ordered sections + 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_keywords(strings: Iterable[str] = ()): + """Parse keywords specifications from the given list of strings. + + >>> kw = sorted(parse_keywords(['_', 'dgettext:2', 'dngettext:2,3', 'pgettext:1c,2']).items()) + >>> for keyword, indices in kw: + ... print((keyword, indices)) + ('_', None) + ('dgettext', (2,)) + ('dngettext', (2, 3)) + ('pgettext', ((1, 'c'), 2)) + """ + keywords = {} + for string in strings: + if ':' in string: + funcname, indices = string.split(':') + else: + funcname, indices = string, None + if funcname not in keywords: + if indices: + inds = [] + for x in indices.split(','): + if x[-1] == 'c': + inds.append((int(x[:-1]), 'c')) + else: + inds.append(int(x)) + indices = tuple(inds) + keywords[funcname] = indices + return keywords + + +if __name__ == '__main__': + main() diff --git a/venv/lib/python3.10/site-packages/babel/messages/jslexer.py b/venv/lib/python3.10/site-packages/babel/messages/jslexer.py new file mode 100644 index 000000000..d3389d076 --- /dev/null +++ b/venv/lib/python3.10/site-packages/babel/messages/jslexer.py @@ -0,0 +1,203 @@ +""" + babel.messages.jslexer + ~~~~~~~~~~~~~~~~~~~~~~ + + A simple JavaScript 1.5 lexer which is used for the JavaScript + extractor. + + :copyright: (c) 2013-2023 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: + # When having a tough decision, use the result that decoded as many multi-byte as possible. + if chaos_difference == 0.0 and self.coherence == other.coherence: + return self.multi_byte_usage > other.multi_byte_usage + return self.coherence > other.coherence + + 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 "".format(self.encoding, self.fingerprint) + + 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[Optional[str]] = [ + 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 + self._output_payload = str(self).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: Optional[List[CharsetMatch]] = None): + self._results: List[CharsetMatch] = sorted(results) if results else [] + + def __iter__(self) -> Iterator[CharsetMatch]: + yield from self._results + + def __getitem__(self, item: Union[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) -> Optional["CharsetMatch"]: + """ + Simply return the first match. Strict equivalent to matches[0]. + """ + if not self._results: + return None + return self._results[0] + + def first(self) -> Optional["CharsetMatch"]: + """ + 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: Optional[str], + encoding_aliases: List[str], + alternative_encodings: List[str], + language: str, + alphabets: List[str], + has_sig_or_bom: bool, + chaos: float, + coherence: float, + unicode_path: Optional[str], + is_preferred: bool, + ): + self.path: str = path + self.unicode_path: Optional[str] = unicode_path + self.encoding: Optional[str] = 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.10/site-packages/charset_normalizer/py.typed b/venv/lib/python3.10/site-packages/charset_normalizer/py.typed new file mode 100644 index 000000000..e69de29bb diff --git a/venv/lib/python3.10/site-packages/charset_normalizer/utils.py b/venv/lib/python3.10/site-packages/charset_normalizer/utils.py new file mode 100644 index 000000000..76eafc646 --- /dev/null +++ b/venv/lib/python3.10/site-packages/charset_normalizer/utils.py @@ -0,0 +1,414 @@ +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, List, Optional, Set, Tuple, Union + +from _multibytecodec import MultibyteIncrementalDecoder + +from .constant import ( + ENCODING_MARKS, + IANA_SUPPORTED_SIMILAR, + RE_POSSIBLE_ENCODING_INDICATION, + UNICODE_RANGES_COMBINED, + UNICODE_SECONDARY_RANGE_KEYWORD, + UTF8_MAXIMAL_ALLOCATION, +) + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_accentuated(character: str) -> bool: + try: + description: str = unicodedata.name(character) + except ValueError: + 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 + ) + + +@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) -> Optional[str]: + """ + 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: + return False + return "LATIN" in description + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_ascii(character: str) -> bool: + try: + character.encode("ascii") + except UnicodeEncodeError: + return False + return True + + +@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: Optional[str] = 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: Optional[str] = unicode_range(character) + + if character_range is None: + return False + + return "Forms" in character_range + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_emoticon(character: str) -> bool: + character_range: Optional[str] = unicode_range(character) + + if character_range is None: + return False + + return "Emoticons" 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 + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_case_variable(character: str) -> bool: + return character.islower() != character.isupper() + + +def is_private_use_only(character: str) -> bool: + character_category: str = unicodedata.category(character) + + return character_category == "Co" + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_cjk(character: str) -> bool: + try: + character_name = unicodedata.name(character) + except ValueError: + 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: + 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: + 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: + 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: + return False + + return "THAI" in character_name + + +@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 = 4096) -> Optional[str]: + """ + 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("encodings.{}".format(name)).IncrementalDecoder, + MultibyteIncrementalDecoder, + ) + + +def identify_sig_or_bom(sequence: bytes) -> Tuple[Optional[str], bytes]: + """ + Identify and extract SIG/BOM in given sequence. + """ + + for iana_encoding in ENCODING_MARKS: + marks: Union[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: + 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("Unable to retrieve IANA for '{}'".format(cp_name)) + + return cp_name + + +def range_scan(decoded_sequence: str) -> List[str]: + ranges: Set[str] = set() + + for character in decoded_sequence: + character_range: Optional[str] = unicode_range(character) + + if character_range is None: + continue + + ranges.add(character_range) + + return list(ranges) + + +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( + "encodings.{}".format(iana_name_a) + ).IncrementalDecoder + decoder_b = importlib.import_module( + "encodings.{}".format(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: Optional[str] = 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.10/site-packages/charset_normalizer/version.py b/venv/lib/python3.10/site-packages/charset_normalizer/version.py new file mode 100644 index 000000000..b74c2643d --- /dev/null +++ b/venv/lib/python3.10/site-packages/charset_normalizer/version.py @@ -0,0 +1,6 @@ +""" +Expose version +""" + +__version__ = "3.1.0" +VERSION = __version__.split(".") diff --git a/venv/lib/python3.10/site-packages/click-8.1.3.dist-info/INSTALLER b/venv/lib/python3.10/site-packages/click-8.1.3.dist-info/INSTALLER new file mode 100644 index 000000000..a1b589e38 --- /dev/null +++ b/venv/lib/python3.10/site-packages/click-8.1.3.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.10/site-packages/click-8.1.3.dist-info/LICENSE.rst b/venv/lib/python3.10/site-packages/click-8.1.3.dist-info/LICENSE.rst new file mode 100644 index 000000000..d12a84918 --- /dev/null +++ b/venv/lib/python3.10/site-packages/click-8.1.3.dist-info/LICENSE.rst @@ -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.10/site-packages/click-8.1.3.dist-info/METADATA b/venv/lib/python3.10/site-packages/click-8.1.3.dist-info/METADATA new file mode 100644 index 000000000..8e5dc1e0a --- /dev/null +++ b/venv/lib/python3.10/site-packages/click-8.1.3.dist-info/METADATA @@ -0,0 +1,111 @@ +Metadata-Version: 2.1 +Name: click +Version: 8.1.3 +Summary: Composable command line interface toolkit +Home-page: https://palletsprojects.com/p/click/ +Author: Armin Ronacher +Author-email: armin.ronacher@active-4.com +Maintainer: Pallets +Maintainer-email: contact@palletsprojects.com +License: BSD-3-Clause +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Documentation, https://click.palletsprojects.com/ +Project-URL: Changes, https://click.palletsprojects.com/changes/ +Project-URL: Source Code, https://github.com/pallets/click/ +Project-URL: Issue Tracker, https://github.com/pallets/click/issues/ +Project-URL: Twitter, https://twitter.com/PalletsTeam +Project-URL: Chat, https://discord.gg/pallets +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Requires-Python: >=3.7 +Description-Content-Type: text/x-rst +License-File: LICENSE.rst +Requires-Dist: colorama ; platform_system == "Windows" +Requires-Dist: importlib-metadata ; python_version < "3.8" + +\$ 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 + + +Installing +---------- + +Install and update using `pip`_: + +.. code-block:: text + + $ pip install -U click + +.. _pip: https://pip.pypa.io/en/stable/getting-started/ + + +A Simple Example +---------------- + +.. code-block:: 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() + +.. code-block:: text + + $ 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 + + +Links +----- + +- Documentation: https://click.palletsprojects.com/ +- Changes: https://click.palletsprojects.com/changes/ +- PyPI Releases: https://pypi.org/project/click/ +- Source Code: https://github.com/pallets/click +- Issue Tracker: https://github.com/pallets/click/issues +- Website: https://palletsprojects.com/p/click +- Twitter: https://twitter.com/PalletsTeam +- Chat: https://discord.gg/pallets + + diff --git a/venv/lib/python3.10/site-packages/click-8.1.3.dist-info/RECORD b/venv/lib/python3.10/site-packages/click-8.1.3.dist-info/RECORD new file mode 100644 index 000000000..67ff862a2 --- /dev/null +++ b/venv/lib/python3.10/site-packages/click-8.1.3.dist-info/RECORD @@ -0,0 +1,39 @@ +click-8.1.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +click-8.1.3.dist-info/LICENSE.rst,sha256=morRBqOU6FO_4h9C9OctWSgZoigF2ZG18ydQKSkrZY0,1475 +click-8.1.3.dist-info/METADATA,sha256=tFJIX5lOjx7c5LjZbdTPFVDJSgyv9F74XY0XCPp_gnc,3247 +click-8.1.3.dist-info/RECORD,, +click-8.1.3.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92 +click-8.1.3.dist-info/top_level.txt,sha256=J1ZQogalYS4pphY_lPECoNMfw0HzTSrZglC4Yfwo4xA,6 +click/__init__.py,sha256=rQBLutqg-z6m8nOzivIfigDn_emijB_dKv9BZ2FNi5s,3138 +click/__pycache__/__init__.cpython-310.pyc,, +click/__pycache__/_compat.cpython-310.pyc,, +click/__pycache__/_termui_impl.cpython-310.pyc,, +click/__pycache__/_textwrap.cpython-310.pyc,, +click/__pycache__/_winconsole.cpython-310.pyc,, +click/__pycache__/core.cpython-310.pyc,, +click/__pycache__/decorators.cpython-310.pyc,, +click/__pycache__/exceptions.cpython-310.pyc,, +click/__pycache__/formatting.cpython-310.pyc,, +click/__pycache__/globals.cpython-310.pyc,, +click/__pycache__/parser.cpython-310.pyc,, +click/__pycache__/shell_completion.cpython-310.pyc,, +click/__pycache__/termui.cpython-310.pyc,, +click/__pycache__/testing.cpython-310.pyc,, +click/__pycache__/types.cpython-310.pyc,, +click/__pycache__/utils.cpython-310.pyc,, +click/_compat.py,sha256=JIHLYs7Jzz4KT9t-ds4o4jBzLjnwCiJQKqur-5iwCKI,18810 +click/_termui_impl.py,sha256=qK6Cfy4mRFxvxE8dya8RBhLpSC8HjF-lvBc6aNrPdwg,23451 +click/_textwrap.py,sha256=10fQ64OcBUMuK7mFvh8363_uoOxPlRItZBmKzRJDgoY,1353 +click/_winconsole.py,sha256=5ju3jQkcZD0W27WEMGqmEP4y_crUVzPCqsX_FYb7BO0,7860 +click/core.py,sha256=mz87bYEKzIoNYEa56BFAiOJnvt1Y0L-i7wD4_ZecieE,112782 +click/decorators.py,sha256=yo3zvzgUm5q7h5CXjyV6q3h_PJAiUaem178zXwdWUFI,16350 +click/exceptions.py,sha256=7gDaLGuFZBeCNwY9ERMsF2-Z3R9Fvq09Zc6IZSKjseo,9167 +click/formatting.py,sha256=Frf0-5W33-loyY_i9qrwXR8-STnW3m5gvyxLVUdyxyk,9706 +click/globals.py,sha256=TP-qM88STzc7f127h35TD_v920FgfOD2EwzqA0oE8XU,1961 +click/parser.py,sha256=cAEt1uQR8gq3-S9ysqbVU-fdAZNvilxw4ReJ_T1OQMk,19044 +click/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +click/shell_completion.py,sha256=qOp_BeC9esEOSZKyu5G7RIxEUaLsXUX-mTb7hB1r4QY,18018 +click/termui.py,sha256=ACBQVOvFCTSqtD5VREeCAdRtlHd-Imla-Lte4wSfMjA,28355 +click/testing.py,sha256=ptpMYgRY7dVfE3UDgkgwayu9ePw98sQI3D7zZXiCpj4,16063 +click/types.py,sha256=rEb1aZSQKq3ciCMmjpG2Uva9vk498XRL7ThrcK2GRss,35805 +click/utils.py,sha256=33D6E7poH_nrKB-xr-UyDEXnxOcCiQqxuRLtrqeVv6o,18682 diff --git a/venv/lib/python3.10/site-packages/click-8.1.3.dist-info/WHEEL b/venv/lib/python3.10/site-packages/click-8.1.3.dist-info/WHEEL new file mode 100644 index 000000000..becc9a66e --- /dev/null +++ b/venv/lib/python3.10/site-packages/click-8.1.3.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.10/site-packages/click-8.1.3.dist-info/top_level.txt b/venv/lib/python3.10/site-packages/click-8.1.3.dist-info/top_level.txt new file mode 100644 index 000000000..dca9a9096 --- /dev/null +++ b/venv/lib/python3.10/site-packages/click-8.1.3.dist-info/top_level.txt @@ -0,0 +1 @@ +click diff --git a/venv/lib/python3.10/site-packages/click/__init__.py b/venv/lib/python3.10/site-packages/click/__init__.py new file mode 100644 index 000000000..e3ef423b6 --- /dev/null +++ b/venv/lib/python3.10/site-packages/click/__init__.py @@ -0,0 +1,73 @@ +""" +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 .core import Argument as Argument +from .core import BaseCommand as BaseCommand +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 MultiCommand as MultiCommand +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 .parser import OptionParser as OptionParser +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 + +__version__ = "8.1.3" diff --git a/venv/lib/python3.10/site-packages/click/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/click/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 000000000..23cec4873 Binary files /dev/null and b/venv/lib/python3.10/site-packages/click/__pycache__/__init__.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/click/__pycache__/_compat.cpython-310.pyc b/venv/lib/python3.10/site-packages/click/__pycache__/_compat.cpython-310.pyc new file mode 100644 index 000000000..27851622b Binary files /dev/null and b/venv/lib/python3.10/site-packages/click/__pycache__/_compat.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/click/__pycache__/_termui_impl.cpython-310.pyc b/venv/lib/python3.10/site-packages/click/__pycache__/_termui_impl.cpython-310.pyc new file mode 100644 index 000000000..5a1e6707e Binary files /dev/null and b/venv/lib/python3.10/site-packages/click/__pycache__/_termui_impl.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/click/__pycache__/_textwrap.cpython-310.pyc b/venv/lib/python3.10/site-packages/click/__pycache__/_textwrap.cpython-310.pyc new file mode 100644 index 000000000..2a7a3e9b8 Binary files /dev/null and b/venv/lib/python3.10/site-packages/click/__pycache__/_textwrap.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/click/__pycache__/_winconsole.cpython-310.pyc b/venv/lib/python3.10/site-packages/click/__pycache__/_winconsole.cpython-310.pyc new file mode 100644 index 000000000..91ea88b38 Binary files /dev/null and b/venv/lib/python3.10/site-packages/click/__pycache__/_winconsole.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/click/__pycache__/core.cpython-310.pyc b/venv/lib/python3.10/site-packages/click/__pycache__/core.cpython-310.pyc new file mode 100644 index 000000000..655da24c9 Binary files /dev/null and b/venv/lib/python3.10/site-packages/click/__pycache__/core.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/click/__pycache__/decorators.cpython-310.pyc b/venv/lib/python3.10/site-packages/click/__pycache__/decorators.cpython-310.pyc new file mode 100644 index 000000000..b7515ea58 Binary files /dev/null and b/venv/lib/python3.10/site-packages/click/__pycache__/decorators.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/click/__pycache__/exceptions.cpython-310.pyc b/venv/lib/python3.10/site-packages/click/__pycache__/exceptions.cpython-310.pyc new file mode 100644 index 000000000..2912b0437 Binary files /dev/null and b/venv/lib/python3.10/site-packages/click/__pycache__/exceptions.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/click/__pycache__/formatting.cpython-310.pyc b/venv/lib/python3.10/site-packages/click/__pycache__/formatting.cpython-310.pyc new file mode 100644 index 000000000..2478308bb Binary files /dev/null and b/venv/lib/python3.10/site-packages/click/__pycache__/formatting.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/click/__pycache__/globals.cpython-310.pyc b/venv/lib/python3.10/site-packages/click/__pycache__/globals.cpython-310.pyc new file mode 100644 index 000000000..a9ea9e6cb Binary files /dev/null and b/venv/lib/python3.10/site-packages/click/__pycache__/globals.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/click/__pycache__/parser.cpython-310.pyc b/venv/lib/python3.10/site-packages/click/__pycache__/parser.cpython-310.pyc new file mode 100644 index 000000000..9236fd2af Binary files /dev/null and b/venv/lib/python3.10/site-packages/click/__pycache__/parser.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/click/__pycache__/shell_completion.cpython-310.pyc b/venv/lib/python3.10/site-packages/click/__pycache__/shell_completion.cpython-310.pyc new file mode 100644 index 000000000..1b6325e3b Binary files /dev/null and b/venv/lib/python3.10/site-packages/click/__pycache__/shell_completion.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/click/__pycache__/termui.cpython-310.pyc b/venv/lib/python3.10/site-packages/click/__pycache__/termui.cpython-310.pyc new file mode 100644 index 000000000..e3638f541 Binary files /dev/null and b/venv/lib/python3.10/site-packages/click/__pycache__/termui.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/click/__pycache__/testing.cpython-310.pyc b/venv/lib/python3.10/site-packages/click/__pycache__/testing.cpython-310.pyc new file mode 100644 index 000000000..f32b46ee2 Binary files /dev/null and b/venv/lib/python3.10/site-packages/click/__pycache__/testing.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/click/__pycache__/types.cpython-310.pyc b/venv/lib/python3.10/site-packages/click/__pycache__/types.cpython-310.pyc new file mode 100644 index 000000000..70ec3c7cf Binary files /dev/null and b/venv/lib/python3.10/site-packages/click/__pycache__/types.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/click/__pycache__/utils.cpython-310.pyc b/venv/lib/python3.10/site-packages/click/__pycache__/utils.cpython-310.pyc new file mode 100644 index 000000000..d53a1f163 Binary files /dev/null and b/venv/lib/python3.10/site-packages/click/__pycache__/utils.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/click/_compat.py b/venv/lib/python3.10/site-packages/click/_compat.py new file mode 100644 index 000000000..766d286be --- /dev/null +++ b/venv/lib/python3.10/site-packages/click/_compat.py @@ -0,0 +1,626 @@ +import codecs +import io +import os +import re +import sys +import typing as t +from weakref import WeakKeyDictionary + +CYGWIN = sys.platform.startswith("cygwin") +MSYS2 = sys.platform.startswith("win") and ("GCC" in sys.version) +# Determine local App Engine environment, per Google's own suggestion +APP_ENGINE = "APPENGINE_RUNTIME" in os.environ and "Development/" in os.environ.get( + "SERVER_SOFTWARE", "" +) +WIN = sys.platform.startswith("win") and not APP_ENGINE and not MSYS2 +auto_wrap_for_ansi: t.Optional[t.Callable[[t.TextIO], t.TextIO]] = None +_ansi_re = re.compile(r"\033\[[;?0-9]*[a-zA-Z]") + + +def get_filesystem_encoding() -> str: + return sys.getfilesystemencoding() or sys.getdefaultencoding() + + +def _make_text_stream( + stream: t.BinaryIO, + encoding: t.Optional[str], + errors: t.Optional[str], + 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) -> 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: t.Optional[str], + errors: t.Optional[str], + 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("") # type: ignore + 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, 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, 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.Optional[t.BinaryIO]: + # 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.Optional[t.BinaryIO]: + # 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: t.Optional[str]) -> 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: t.Optional[str], errors: t.Optional[str] +) -> 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, + encoding: t.Optional[str], + errors: t.Optional[str], + is_binary: t.Callable[[t.IO, bool], bool], + find_binary: t.Callable[[t.IO], t.Optional[t.BinaryIO]], + 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, + encoding: t.Optional[str], + errors: t.Optional[str], + 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, + encoding: t.Optional[str], + errors: t.Optional[str], + 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: t.Optional[str] = None, errors: t.Optional[str] = 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: t.Optional[str] = None, errors: t.Optional[str] = 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: t.Optional[str] = None, errors: t.Optional[str] = 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: t.Union[str, os.PathLike, int], + mode: str, + encoding: t.Optional[str], + errors: t.Optional[str], +) -> t.IO: + """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, + mode: str = "r", + encoding: t.Optional[str] = None, + errors: t.Optional[str] = "strict", + atomic: bool = False, +) -> t.Tuple[t.IO, bool]: + binary = "b" in mode + + # 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: t.Optional[int] = 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, af), True + + +class _AtomicFile: + def __init__(self, f: t.IO, 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, exc_value, tb): # type: ignore + 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) -> bool: + while isinstance(stream, (_FixupStream, _NonClosingTextIOWrapper)): + stream = stream._stream + + return stream.__class__.__module__.startswith("ipykernel.") + + +def should_strip_ansi( + stream: t.Optional[t.IO] = None, color: t.Optional[bool] = 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: t.MutableMapping[t.TextIO, t.TextIO] = WeakKeyDictionary() + + def auto_wrap_for_ansi( + stream: t.TextIO, color: t.Optional[bool] = 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): + try: + return _write(s) + except BaseException: + ansi_wrapper.reset_all() + raise + + rv.write = _safe_write + + try: + _ansi_stream_wrappers[stream] = rv + except Exception: + pass + + return rv + +else: + + def _get_argv_encoding() -> str: + return getattr(sys.stdin, "encoding", None) or get_filesystem_encoding() + + def _get_windows_console_stream( + f: t.TextIO, encoding: t.Optional[str], errors: t.Optional[str] + ) -> t.Optional[t.TextIO]: + return None + + +def term_len(x: str) -> int: + return len(strip_ansi(x)) + + +def isatty(stream: t.IO) -> bool: + try: + return stream.isatty() + except Exception: + return False + + +def _make_cached_stream_func( + src_func: t.Callable[[], t.TextIO], wrapper_func: t.Callable[[], t.TextIO] +) -> t.Callable[[], t.TextIO]: + cache: t.MutableMapping[t.TextIO, t.TextIO] = WeakKeyDictionary() + + def func() -> t.TextIO: + stream = src_func() + 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: t.Mapping[str, t.Callable[[], t.BinaryIO]] = { + "stdin": get_binary_stdin, + "stdout": get_binary_stdout, + "stderr": get_binary_stderr, +} + +text_streams: t.Mapping[ + str, t.Callable[[t.Optional[str], t.Optional[str]], t.TextIO] +] = { + "stdin": get_text_stdin, + "stdout": get_text_stdout, + "stderr": get_text_stderr, +} diff --git a/venv/lib/python3.10/site-packages/click/_termui_impl.py b/venv/lib/python3.10/site-packages/click/_termui_impl.py new file mode 100644 index 000000000..4b979bcc1 --- /dev/null +++ b/venv/lib/python3.10/site-packages/click/_termui_impl.py @@ -0,0 +1,717 @@ +""" +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. +""" +import contextlib +import math +import os +import sys +import time +import typing as t +from gettext import gettext as _ + +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: t.Optional[t.Iterable[V]], + length: t.Optional[int] = None, + fill_char: str = "#", + empty_char: str = " ", + bar_template: str = "%(bar)s", + info_sep: str = " ", + show_eta: bool = True, + show_percent: t.Optional[bool] = None, + show_pos: bool = False, + item_show_func: t.Optional[t.Callable[[t.Optional[V]], t.Optional[str]]] = None, + label: t.Optional[str] = None, + file: t.Optional[t.TextIO] = None, + color: t.Optional[bool] = 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.show_eta = show_eta + self.show_percent = show_percent + self.show_pos = show_pos + self.item_show_func = item_show_func + self.label = label or "" + if file is None: + file = _default_text_stdout() + self.file = file + self.color = color + self.update_min_steps = update_min_steps + self._completed_intervals = 0 + self.width = width + self.autowidth = 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(t.Iterable[V], range(length)) + self.iter = iter(iterable) + self.length = length + self.pos = 0 + self.avg: t.List[float] = [] + self.start = self.last_eta = time.time() + self.eta_known = False + self.finished = False + self.max_width: t.Optional[int] = None + self.entered = False + self.current_item: t.Optional[V] = None + self.is_hidden = not isatty(self.file) + self._last_line: t.Optional[str] = None + + def __enter__(self) -> "ProgressBar": + self.entered = True + self.render_progress() + return self + + def __exit__(self, exc_type, exc_value, tb): # type: ignore + self.render_finish() + + def __iter__(self) -> t.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.is_hidden: + 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.is_hidden: + # Only output the label as it changes if the output is not a + # TTY. Use file=stderr if you expect to be piping stdout. + 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: + buf.append(BEFORE_BAR) + buf.append(" " * self.max_width) # type: ignore + 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: t.Optional[V] = 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) -> t.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 self.is_hidden: + 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: t.Iterable[str], color: t.Optional[bool] = None) -> None: + """Decide what method to use for paging through text.""" + stdout = _default_text_stdout() + if not isatty(sys.stdin) or not isatty(stdout): + return _nullpager(stdout, generator, color) + pager_cmd = (os.environ.get("PAGER", None) or "").strip() + if pager_cmd: + if WIN: + return _tempfilepager(generator, pager_cmd, color) + return _pipepager(generator, pager_cmd, color) + if os.environ.get("TERM") in ("dumb", "emacs"): + return _nullpager(stdout, generator, color) + if WIN or sys.platform.startswith("os2"): + return _tempfilepager(generator, "more <", color) + if hasattr(os, "system") and os.system("(less) 2>/dev/null") == 0: + return _pipepager(generator, "less", color) + + import tempfile + + fd, filename = tempfile.mkstemp() + os.close(fd) + try: + if hasattr(os, "system") and os.system(f'more "{filename}"') == 0: + return _pipepager(generator, "more", color) + return _nullpager(stdout, generator, color) + finally: + os.unlink(filename) + + +def _pipepager(generator: t.Iterable[str], cmd: str, color: t.Optional[bool]) -> None: + """Page through text by feeding it to another program. Invoking a + pager through this might support colors. + """ + import subprocess + + env = dict(os.environ) + + # If we're piping to less we might support colors under the + # condition that + cmd_detail = cmd.rsplit("/", 1)[-1].split() + if color is None and cmd_detail[0] == "less": + less_flags = f"{os.environ.get('LESS', '')}{' '.join(cmd_detail[1:])}" + if not less_flags: + env["LESS"] = "-R" + color = True + elif "r" in less_flags or "R" in less_flags: + color = True + + c = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, env=env) + stdin = t.cast(t.BinaryIO, c.stdin) + encoding = get_best_encoding(stdin) + try: + for text in generator: + if not color: + text = strip_ansi(text) + + stdin.write(text.encode(encoding, "replace")) + except (OSError, KeyboardInterrupt): + pass + else: + stdin.close() + + # 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 + + +def _tempfilepager( + generator: t.Iterable[str], cmd: str, color: t.Optional[bool] +) -> None: + """Page through text by invoking a program on a temporary file.""" + 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: + os.system(f'{cmd} "{filename}"') + finally: + os.close(fd) + os.unlink(filename) + + +def _nullpager( + stream: t.TextIO, generator: t.Iterable[str], color: t.Optional[bool] +) -> 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: t.Optional[str] = None, + env: t.Optional[t.Mapping[str, str]] = 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 os.system(f"which {editor} >/dev/null 2>&1") == 0: + return editor + return "vi" + + def edit_file(self, filename: str) -> None: + import subprocess + + editor = self.get_editor() + environ: t.Optional[t.Dict[str, str]] = None + + if self.env: + environ = os.environ.copy() + environ.update(self.env) + + try: + c = subprocess.Popen(f'{editor} "{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 + + def edit(self, text: t.Optional[t.AnyStr]) -> t.Optional[t.AnyStr]: + import tempfile + + if not text: + 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_file(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") # type: ignore + 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.replace('"', "")) + args = f'explorer /select,"{url}"' + else: + url = url.replace('"', "") + wait_str = "/WAIT" if wait else "" + args = f'start {wait_str} "" "{url}"' + return os.system(args) + elif CYGWIN: + if locate: + url = os.path.dirname(_unquote_file(url).replace('"', "")) + args = f'cygstart "{url}"' + else: + url = url.replace('"', "") + wait_str = "-w" if wait else "" + args = f'cygstart {wait_str} "{url}"' + return os.system(args) + + 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) -> t.Optional[BaseException]: + 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 WIN: + import msvcrt + + @contextlib.contextmanager + def raw_terminal() -> t.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`. + func: t.Callable[[], str] + + if echo: + func = msvcrt.getwche # type: ignore + else: + func = msvcrt.getwch # type: ignore + + 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 tty + import termios + + @contextlib.contextmanager + def raw_terminal() -> t.Iterator[int]: + f: t.Optional[t.TextIO] + 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.10/site-packages/click/_textwrap.py b/venv/lib/python3.10/site-packages/click/_textwrap.py new file mode 100644 index 000000000..b47dcbd42 --- /dev/null +++ b/venv/lib/python3.10/site-packages/click/_textwrap.py @@ -0,0 +1,49 @@ +import textwrap +import typing as t +from contextlib import contextmanager + + +class TextWrapper(textwrap.TextWrapper): + def _handle_long_word( + self, + reversed_chunks: t.List[str], + cur_line: t.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) -> t.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.10/site-packages/click/_winconsole.py b/venv/lib/python3.10/site-packages/click/_winconsole.py new file mode 100644 index 000000000..6b20df315 --- /dev/null +++ b/venv/lib/python3.10/site-packages/click/_winconsole.py @@ -0,0 +1,279 @@ +# 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. +import io +import sys +import time +import typing as t +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 + +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_ = [ + ("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, writable=False): + buf = Py_buffer() + flags = PyBUF_WRITABLE if writable else PyBUF_SIMPLE + PyObject_GetBuffer(py_object(obj), byref(buf), flags) + + try: + buffer_type = c_char * buf.len + return buffer_type.from_address(buf.buf) + finally: + PyBuffer_Release(byref(buf)) + + +class _WindowsConsoleRawIOBase(io.RawIOBase): + def __init__(self, handle): + self.handle = handle + + def isatty(self): + super().isatty() + return True + + +class _WindowsConsoleReader(_WindowsConsoleRawIOBase): + def readable(self): + return True + + def readinto(self, b): + 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): + return True + + @staticmethod + def _get_error_message(errno): + 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): + 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: t.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): + 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: t.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: t.Optional[str], errors: t.Optional[str] +) -> t.Optional[t.TextIO]: + if ( + get_buffer is not None + and encoding in {"utf-16-le", None} + and errors in {"strict", None} + and _is_console(f) + ): + func = _stream_factories.get(f.fileno()) + if func is not None: + b = getattr(f, "buffer", None) + + if b is None: + return None + + return func(b) diff --git a/venv/lib/python3.10/site-packages/click/core.py b/venv/lib/python3.10/site-packages/click/core.py new file mode 100644 index 000000000..5abfb0f3c --- /dev/null +++ b/venv/lib/python3.10/site-packages/click/core.py @@ -0,0 +1,2998 @@ +import enum +import errno +import inspect +import os +import sys +import typing as t +from collections import abc +from contextlib import contextmanager +from contextlib import ExitStack +from functools import partial +from functools import update_wrapper +from gettext import gettext as _ +from gettext import ngettext +from itertools import repeat + +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 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: + import typing_extensions as te + 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 +) -> t.Iterator[t.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(MultiCommand, 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_multicommand( + base_command: "MultiCommand", cmd_name: str, cmd: "Command", register: bool = False +) -> None: + if not base_command.chain or not isinstance(cmd, MultiCommand): + return + if register: + hint = ( + "It is not possible to add multi commands as children to" + " another multi command that is in chain mode." + ) + else: + hint = ( + "Found a multi command as subcommand to a multi command" + " that is in chain mode. This is not supported." + ) + raise RuntimeError( + f"{hint}. Command {base_command.name!r} is set to chain and" + f" {cmd_name!r} was added as a subcommand but it in itself is a" + f" multi command. ({cmd_name!r} is a {type(cmd).__name__}" + f" within a chained {type(base_command).__name__} named" + f" {base_command.name!r})." + ) + + +def batch(iterable: t.Iterable[V], batch_size: int) -> t.List[t.Tuple[V, ...]]: + return list(zip(*repeat(iter(iterable), batch_size))) + + +@contextmanager +def augment_usage_errors( + ctx: "Context", param: t.Optional["Parameter"] = None +) -> t.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: t.Sequence["Parameter"], + declaration_order: t.Sequence["Parameter"], +) -> t.List["Parameter"]: + """Given a sequence of parameters in the order as should be considered + for processing and an iterable of parameters that exist, this returns + a list in the correct order as they should be processed. + """ + + def sort_key(item: "Parameter") -> t.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.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: t.Type["HelpFormatter"] = HelpFormatter + + def __init__( + self, + command: "Command", + parent: t.Optional["Context"] = None, + info_name: t.Optional[str] = None, + obj: t.Optional[t.Any] = None, + auto_envvar_prefix: t.Optional[str] = None, + default_map: t.Optional[t.Dict[str, t.Any]] = None, + terminal_width: t.Optional[int] = None, + max_content_width: t.Optional[int] = None, + resilient_parsing: bool = False, + allow_extra_args: t.Optional[bool] = None, + allow_interspersed_args: t.Optional[bool] = None, + ignore_unknown_options: t.Optional[bool] = None, + help_option_names: t.Optional[t.List[str]] = None, + token_normalize_func: t.Optional[t.Callable[[str], str]] = None, + color: t.Optional[bool] = None, + show_default: t.Optional[bool] = 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: t.Dict[str, t.Any] = {} + #: the leftover arguments. + self.args: t.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: t.List[str] = [] + #: the collected prefixes of the command's options. + self._opt_prefixes: t.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: t.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: t.Optional[t.Dict[str, t.Any]] = 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: t.Optional[str] = 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: t.Optional[int] = 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: t.Optional[int] = 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: t.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.Optional[ + t.Callable[[str], str] + ] = 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: t.Optional[str] = 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: t.Optional[bool] = 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: t.Optional[bool] = show_default + + self._close_callbacks: t.List[t.Callable[[], t.Any]] = [] + self._depth = 0 + self._parameter_source: t.Dict[str, ParameterSource] = {} + self._exit_stack = ExitStack() + + def to_info_dict(self) -> t.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, exc_value, tb): # type: ignore + self._depth -= 1 + if self._depth == 0: + self.close() + pop_context() + + @contextmanager + def scope(self, cleanup: bool = True) -> t.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) -> t.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: t.ContextManager[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: t.Type[V]) -> t.Optional[V]: + """Finds the closest object of a given type.""" + node: t.Optional["Context"] = 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: t.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: "te.Literal[True]" = True + ) -> t.Optional[t.Any]: + ... + + @t.overload + def lookup_default( + self, name: str, call: "te.Literal[False]" = ... + ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: + ... + + def lookup_default(self, name: str, call: bool = True) -> t.Optional[t.Any]: + """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) -> "te.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) -> "te.NoReturn": + """Aborts the script.""" + raise Abort() + + def exit(self, code: int = 0) -> "te.NoReturn": + """Exits the application with a given exit code.""" + 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) + + def invoke( + __self, # noqa: B902 + __callback: t.Union["Command", t.Callable[..., t.Any]], + *args: t.Any, + **kwargs: t.Any, + ) -> t.Any: + """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. + + Note that before Click 3.2 keyword arguments were not properly filled + in against the intention of this code and no context was created. For + more information about this change and why it was done in a bugfix + release see :ref:`upgrade-to-3.2`. + + .. versionchanged:: 8.0 + All ``kwargs`` are tracked in :attr:`params` so they will be + passed if :meth:`forward` is called at multiple levels. + """ + 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 = 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 # noqa: B902 + ) -> 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) -> t.Optional[ParameterSource]: + """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 BaseCommand: + """The base command implements the minimal API contract of commands. + Most code will never use this as it does not implement a lot of useful + functionality but it can act as the direct subclass of alternative + parsing methods that do not depend on the Click parser. + + For instance, this can be used to bridge Click and other systems like + argparse or docopt. + + Because base commands do not implement a lot of the API that other + parts of Click take for granted, they are not supported for all + operations. For instance, they cannot be used with the decorators + usually and they have no built-in callback system. + + .. versionchanged:: 2.0 + Added the `context_settings` parameter. + + :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. + """ + + #: The context class to create with :meth:`make_context`. + #: + #: .. versionadded:: 8.0 + context_class: t.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: t.Optional[str], + context_settings: t.Optional[t.Dict[str, t.Any]] = None, + ) -> 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: t.Dict[str, t.Any] = context_settings + + def to_info_dict(self, ctx: Context) -> t.Dict[str, t.Any]: + """Gather information that could be useful for a tool generating + user-facing documentation. This traverses the entire structure + below this command. + + Use :meth:`click.Context.to_info_dict` to traverse the entire + CLI structure. + + :param ctx: A :class:`Context` representing this command. + + .. versionadded:: 8.0 + """ + return {"name": self.name} + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} {self.name}>" + + def get_usage(self, ctx: Context) -> str: + raise NotImplementedError("Base commands cannot get usage") + + def get_help(self, ctx: Context) -> str: + raise NotImplementedError("Base commands cannot get help") + + def make_context( + self, + info_name: t.Optional[str], + args: t.List[str], + parent: t.Optional[Context] = 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 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 # type: ignore + ) + + with ctx.scope(cleanup=False): + self.parse_args(ctx, args) + return ctx + + def parse_args(self, ctx: Context, args: t.List[str]) -> t.List[str]: + """Given a context and a list of arguments this creates the parser + and parses the arguments, then modifies the context as necessary. + This is automatically invoked by :meth:`make_context`. + """ + raise NotImplementedError("Base commands do not know how to parse arguments.") + + def invoke(self, ctx: Context) -> t.Any: + """Given a context, this invokes the command. The default + implementation is raising a not implemented error. + """ + raise NotImplementedError("Base commands are not invokable by default") + + def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: + """Return a list of completions for the incomplete value. Looks + at the names of chained multi-commands. + + Any command could be part of a chained multi-command, so sibling + commands are valid at any point during command completion. Other + command classes will return more completions. + + :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: t.List["CompletionItem"] = [] + + while ctx.parent is not None: + ctx = ctx.parent + + if isinstance(ctx.command, MultiCommand) 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: t.Optional[t.Sequence[str]] = None, + prog_name: t.Optional[str] = None, + complete_var: t.Optional[str] = None, + standalone_mode: "te.Literal[True]" = True, + **extra: t.Any, + ) -> "te.NoReturn": + ... + + @t.overload + def main( + self, + args: t.Optional[t.Sequence[str]] = None, + prog_name: t.Optional[str] = None, + complete_var: t.Optional[str] = None, + standalone_mode: bool = ..., + **extra: t.Any, + ) -> t.Any: + ... + + def main( + self, + args: t.Optional[t.Sequence[str]] = None, + prog_name: t.Optional[str] = None, + complete_var: t.Optional[str] = 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): + echo(file=sys.stderr) + raise Abort() from None + 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: t.Dict[str, t.Any], + prog_name: str, + complete_var: t.Optional[str] = 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``. + """ + if complete_var is None: + complete_var = f"_{prog_name}_COMPLETE".replace("-", "_").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 Command(BaseCommand): + """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: issues a message indicating that + the command is deprecated. + + .. 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. + """ + + def __init__( + self, + name: t.Optional[str], + context_settings: t.Optional[t.Dict[str, t.Any]] = None, + callback: t.Optional[t.Callable[..., t.Any]] = None, + params: t.Optional[t.List["Parameter"]] = None, + help: t.Optional[str] = None, + epilog: t.Optional[str] = None, + short_help: t.Optional[str] = None, + options_metavar: t.Optional[str] = "[OPTIONS]", + add_help_option: bool = True, + no_args_is_help: bool = False, + hidden: bool = False, + deprecated: bool = False, + ) -> None: + super().__init__(name, 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: t.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.no_args_is_help = no_args_is_help + self.hidden = hidden + self.deprecated = deprecated + + def to_info_dict(self, ctx: Context) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict(ctx) + info_dict.update( + 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, + ) + return info_dict + + 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) -> t.List["Parameter"]: + rv = self.params + help_option = self.get_help_option(ctx) + + if help_option is not None: + rv = [*rv, help_option] + + return rv + + 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) -> t.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) -> t.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) -> t.Optional["Option"]: + """Returns the help option object.""" + help_options = self.get_help_option_names(ctx) + + if not help_options or not self.add_help_option: + return None + + def show_help(ctx: Context, param: "Parameter", value: str) -> None: + if value and not ctx.resilient_parsing: + echo(ctx.get_help(), color=ctx.color) + ctx.exit() + + return Option( + help_options, + is_flag=True, + is_eager=True, + expose_value=False, + callback=show_help, + help=_("Show this message and exit."), + ) + + 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: + text = _("(Deprecated) {text}").format(text=text) + + 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.""" + text = self.help if self.help is not None else "" + + if self.deprecated: + text = _("(Deprecated) {text}").format(text=text) + + if text: + text = inspect.cleandoc(text).partition("\f")[0] + 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 parse_args(self, ctx: Context, args: t.List[str]) -> t.List[str]: + if not args and self.no_args_is_help and not ctx.resilient_parsing: + echo(ctx.get_help(), color=ctx.color) + ctx.exit() + + 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: + message = _( + "DeprecationWarning: The command {name!r} is deprecated." + ).format(name=self.name) + 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) -> t.List["CompletionItem"]: + """Return a list of completions for the incomplete value. Looks + at the names of options 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: t.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) + ) + + results.extend(super().shell_complete(ctx, incomplete)) + return results + + +class MultiCommand(Command): + """A multi command is the basic implementation of a command that + dispatches to subcommands. The most common version is the + :class:`Group`. + + :param invoke_without_command: this controls how the multi command itself + is invoked. By default it's only invoked + if a subcommand is provided. + :param no_args_is_help: this controls what happens if no arguments are + provided. This option is enabled by default if + `invoke_without_command` is disabled or disabled + if it's enabled. If enabled this will add + ``--help`` as argument if no arguments are + passed. + :param subcommand_metavar: the string that is used in the documentation + to indicate the subcommand place. + :param chain: if this is set to `True` chaining of multiple subcommands + is enabled. This restricts the form of commands in that + they cannot have optional arguments but it allows + multiple commands to be chained together. + :param result_callback: The result callback to attach to this multi + command. This can be set or changed later with the + :meth:`result_callback` decorator. + """ + + allow_extra_args = True + allow_interspersed_args = False + + def __init__( + self, + name: t.Optional[str] = None, + invoke_without_command: bool = False, + no_args_is_help: t.Optional[bool] = None, + subcommand_metavar: t.Optional[str] = None, + chain: bool = False, + result_callback: t.Optional[t.Callable[..., t.Any]] = None, + **attrs: t.Any, + ) -> None: + super().__init__(name, **attrs) + + 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( + "Multi commands in chain mode cannot have" + " optional arguments." + ) + + def to_info_dict(self, ctx: Context) -> t.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 collect_usage_pieces(self, ctx: Context) -> t.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 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, *args, **kwargs): # type: ignore + inner = old_callback(__value, *args, **kwargs) # type: ignore + return f(inner, *args, **kwargs) + + self._result_callback = rv = update_wrapper(t.cast(F, function), f) + return rv + + return decorator + + 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: t.List[str]) -> t.List[str]: + if not args and self.no_args_is_help and not ctx.resilient_parsing: + echo(ctx.get_help(), color=ctx.color) + ctx.exit() + + 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: t.List[str] + ) -> t.Tuple[t.Optional[str], t.Optional[Command], t.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, 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 get_command(self, ctx: Context, cmd_name: str) -> t.Optional[Command]: + """Given a context and a command name, this returns a + :class:`Command` object if it exists or returns `None`. + """ + raise NotImplementedError + + def list_commands(self, ctx: Context) -> t.List[str]: + """Returns a list of subcommand names in the order they should + appear. + """ + return [] + + def shell_complete(self, ctx: Context, incomplete: str) -> t.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 Group(MultiCommand): + """A group allows a command to have subcommands attached. This is + the most common way to implement nesting in Click. + + :param name: The name of the group command. + :param commands: A dict mapping names to :class:`Command` objects. + Can also be a list of :class:`Command`, which will use + :attr:`Command.name` to create the dict. + :param attrs: Other command arguments described in + :class:`MultiCommand`, :class:`Command`, and + :class:`BaseCommand`. + + .. versionchanged:: 8.0 + The ``commmands`` argument can be a list of command objects. + """ + + #: 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: t.Optional[t.Type[Command]] = 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: t.Optional[t.Union[t.Type["Group"], t.Type[type]]] = None + # Literal[type] isn't valid, so use Type[type] + + def __init__( + self, + name: t.Optional[str] = None, + commands: t.Optional[t.Union[t.Dict[str, Command], t.Sequence[Command]]] = None, + **attrs: t.Any, + ) -> None: + super().__init__(name, **attrs) + + 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: t.Dict[str, Command] = commands + + def add_command(self, cmd: Command, name: t.Optional[str] = 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_multicommand(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.Union[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 + + if self.command_class and kwargs.get("cls") is None: + kwargs["cls"] = self.command_class + + func: t.Optional[t.Callable] = None + + if args and callable(args[0]): + assert ( + len(args) == 1 and not kwargs + ), "Use 'command(**kwargs)(callable)' to provide arguments." + (func,) = args + args = () + + 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.Union[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.Optional[t.Callable] = 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 get_command(self, ctx: Context, cmd_name: str) -> t.Optional[Command]: + return self.commands.get(cmd_name) + + def list_commands(self, ctx: Context) -> t.List[str]: + return sorted(self.commands) + + +class CommandCollection(MultiCommand): + """A command collection is a multi command that merges multiple multi + commands together into one. This is a straightforward implementation + that accepts a list of different multi commands as sources and + provides all the commands for each of them. + """ + + def __init__( + self, + name: t.Optional[str] = None, + sources: t.Optional[t.List[MultiCommand]] = None, + **attrs: t.Any, + ) -> None: + super().__init__(name, **attrs) + #: The list of registered multi commands. + self.sources: t.List[MultiCommand] = sources or [] + + def add_source(self, multi_cmd: MultiCommand) -> None: + """Adds a new multi command to the chain dispatcher.""" + self.sources.append(multi_cmd) + + def get_command(self, ctx: Context, cmd_name: str) -> t.Optional[Command]: + for source in self.sources: + rv = source.get_command(ctx, cmd_name) + + if rv is not None: + if self.chain: + _check_multicommand(self, cmd_name, rv) + + return rv + + return None + + def list_commands(self, ctx: Context) -> t.List[str]: + rv: t.Set[str] = set() + + for source in self.sources: + rv.update(source.list_commands(ctx)) + + return sorted(rv) + + +def _check_iter(value: t.Any) -> t.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 later 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. + + .. 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: t.Optional[t.Sequence[str]] = None, + type: t.Optional[t.Union[types.ParamType, t.Any]] = None, + required: bool = False, + default: t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]] = None, + callback: t.Optional[t.Callable[[Context, "Parameter", t.Any], t.Any]] = None, + nargs: t.Optional[int] = None, + multiple: bool = False, + metavar: t.Optional[str] = None, + expose_value: bool = True, + is_eager: bool = False, + envvar: t.Optional[t.Union[str, t.Sequence[str]]] = None, + shell_complete: t.Optional[ + t.Callable[ + [Context, "Parameter", str], + t.Union[t.List["CompletionItem"], t.List[str]], + ] + ] = None, + ) -> None: + self.name, self.opts, self.secondary_opts = self._parse_decls( + param_decls or (), expose_value + ) + self.type = 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 + + 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}." + ) + + def to_info_dict(self) -> t.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: t.Sequence[str], expose_value: bool + ) -> t.Tuple[t.Optional[str], t.List[str], t.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) -> str: + if self.metavar is not None: + return self.metavar + + metavar = self.type.get_metavar(self) + + 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: "te.Literal[True]" = True + ) -> t.Optional[t.Any]: + ... + + @t.overload + def get_default( + self, ctx: Context, call: bool = ... + ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: + ... + + def get_default( + self, ctx: Context, call: bool = True + ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: + """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: t.Mapping[str, t.Any] + ) -> t.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) -> t.Iterator: + 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: + convert: t.Callable[[t.Any], t.Any] = partial( + self.type, param=self, ctx=ctx + ) + elif self.nargs == -1: + + def convert(value: t.Any) -> t.Tuple: + return tuple(self.type(x, self, ctx) for x in check_iter(value)) + + else: # nargs > 1 + + def convert(value: t.Any) -> t.Tuple: + 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) -> t.Optional[str]: + 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.Optional[t.Any]: + rv: t.Optional[t.Any] = 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: t.Mapping[str, t.Any], args: t.List[str] + ) -> t.Tuple[t.Any, t.List[str]]: + with augment_usage_errors(ctx, param=self): + value, source = self.consume_value(ctx, opts) + 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) -> t.Optional[t.Tuple[str, str]]: + pass + + def get_usage_pieces(self, ctx: Context) -> t.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) -> t.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(t.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. 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. + :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. + + .. versionchanged:: 8.1.0 + Help text indentation is cleaned here instead of only in the + ``@option`` decorator. + + .. versionchanged:: 8.1.0 + The ``show_default`` parameter overrides + ``Context.show_default``. + + .. versionchanged:: 8.1.0 + 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: t.Optional[t.Sequence[str]] = None, + show_default: t.Union[bool, str, None] = None, + prompt: t.Union[bool, str] = False, + confirmation_prompt: t.Union[bool, str] = False, + prompt_required: bool = True, + hide_input: bool = False, + is_flag: t.Optional[bool] = None, + flag_value: t.Optional[t.Any] = None, + multiple: bool = False, + count: bool = False, + allow_from_autoenv: bool = True, + type: t.Optional[t.Union[types.ParamType, t.Any]] = None, + help: t.Optional[str] = None, + hidden: bool = False, + show_choices: bool = True, + show_envvar: bool = 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, **attrs) + + if prompt is True: + if self.name is None: + raise TypeError("'name' is required with 'prompt=True'.") + + prompt_text: t.Optional[str] = self.name.replace("_", " ").capitalize() + elif prompt is False: + prompt_text = None + else: + prompt_text = prompt + + 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 + + if is_flag and default_is_missing and not self.required: + self.default: t.Union[t.Any, t.Callable[[], t.Any]] = False + + if flag_value is None: + flag_value = not self.default + + 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 = 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 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'.") + + if self.multiple and self.is_flag: + raise TypeError("'multiple' is not valid with 'is_flag', use 'count'.") + + def to_info_dict(self) -> t.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 _parse_decls( + self, decls: t.Sequence[str], expose_value: bool + ) -> t.Tuple[t.Optional[str], t.List[str], t.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("Could not determine name for option") + + 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) -> t.Optional[t.Tuple[str, str]]: + if self.hidden: + return None + + any_prefix_is_slash = False + + def _write_opts(opts: t.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()}" + + return rv + + rv = [_write_opts(self.opts)] + + if self.secondary_opts: + rv.append(_write_opts(self.secondary_opts)) + + help = self.help or "" + extra = [] + + 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: + var_str = ( + envvar + if isinstance(envvar, str) + else ", ".join(str(d) for d in envvar) + ) + extra.append(_("env var: {var}").format(var=var_str)) + + # 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 self.default else self.secondary_opts)[0] + )[1] + elif self.is_bool_flag and not self.secondary_opts and not default_value: + default_string = "" + else: + default_string = str(default_value) + + if default_string: + extra.append(_("default: {default}").format(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.append(range_str) + + if self.required: + extra.append(_("required")) + + if extra: + extra_str = "; ".join(extra) + help = f"{help} [{extra_str}]" if help else f"[{extra_str}]" + + return ("; " if any_prefix_is_slash else " / ").join(rv), help + + @t.overload + def get_default( + self, ctx: Context, call: "te.Literal[True]" = True + ) -> t.Optional[t.Any]: + ... + + @t.overload + def get_default( + self, ctx: Context, call: bool = ... + ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: + ... + + def get_default( + self, ctx: Context, call: bool = True + ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: + # 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 param.flag_value # type: ignore + + 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) + + 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), + ) + + def resolve_envvar_value(self, ctx: Context) -> t.Optional[str]: + rv = super().resolve_envvar_value(ctx) + + if rv is not None: + 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.Optional[t.Any]: + rv: t.Optional[t.Any] = 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: t.Mapping[str, "Parameter"] + ) -> t.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 parameter constructor. + """ + + param_type_name = "argument" + + def __init__( + self, + param_decls: t.Sequence[str], + required: t.Optional[bool] = 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) -> str: + if self.metavar is not None: + return self.metavar + var = self.type.get_metavar(self) + if not var: + var = self.name.upper() # type: ignore + if not self.required: + var = f"[{var}]" + if self.nargs != 1: + var += "..." + return var + + def _parse_decls( + self, decls: t.Sequence[str], expose_value: bool + ) -> t.Tuple[t.Optional[str], t.List[str], t.List[str]]: + if not decls: + if not expose_value: + return None, [], [] + raise TypeError("Could not determine name for argument") + 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)}." + ) + return name, [arg], [] + + def get_usage_pieces(self, ctx: Context) -> t.List[str]: + return [self.make_metavar()] + + def get_error_hint(self, ctx: Context) -> str: + return f"'{self.make_metavar()}'" + + def add_to_parser(self, parser: OptionParser, ctx: Context) -> None: + parser.add_argument(dest=self.name, nargs=self.nargs, obj=self) diff --git a/venv/lib/python3.10/site-packages/click/decorators.py b/venv/lib/python3.10/site-packages/click/decorators.py new file mode 100644 index 000000000..28618dc52 --- /dev/null +++ b/venv/lib/python3.10/site-packages/click/decorators.py @@ -0,0 +1,497 @@ +import inspect +import types +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 + +F = t.TypeVar("F", bound=t.Callable[..., t.Any]) +FC = t.TypeVar("FC", bound=t.Union[t.Callable[..., t.Any], Command]) + + +def pass_context(f: F) -> F: + """Marks a callback as wanting to receive the current context + object as first argument. + """ + + def new_func(*args, **kwargs): # type: ignore + return f(get_current_context(), *args, **kwargs) + + return update_wrapper(t.cast(F, new_func), f) + + +def pass_obj(f: F) -> F: + """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, **kwargs): # type: ignore + return f(get_current_context().obj, *args, **kwargs) + + return update_wrapper(t.cast(F, new_func), f) + + +def make_pass_decorator( + object_type: t.Type, ensure: bool = False +) -> "t.Callable[[F], F]": + """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: F) -> F: + def new_func(*args, **kwargs): # type: ignore + ctx = get_current_context() + + 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(t.cast(F, new_func), f) + + return decorator + + +def pass_meta_key( + key: str, *, doc_description: t.Optional[str] = None +) -> "t.Callable[[F], F]": + """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: F) -> F: + def new_func(*args, **kwargs): # type: ignore + ctx = get_current_context() + obj = ctx.meta[key] + return ctx.invoke(f, obj, *args, **kwargs) + + return update_wrapper(t.cast(F, 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) + + +@t.overload +def command( + __func: t.Callable[..., t.Any], +) -> Command: + ... + + +@t.overload +def command( + name: t.Optional[str] = None, + **attrs: t.Any, +) -> t.Callable[..., Command]: + ... + + +@t.overload +def command( + name: t.Optional[str] = None, + cls: t.Type[CmdType] = ..., + **attrs: t.Any, +) -> t.Callable[..., CmdType]: + ... + + +def command( + name: t.Union[str, t.Callable[..., t.Any], None] = None, + cls: t.Optional[t.Type[Command]] = None, + **attrs: t.Any, +) -> t.Union[Command, t.Callable[..., Command]]: + 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 with + underscores replaced by dashes. If you want to change that, you can + pass the intended name as the first argument. + + 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. This defaults to the function + name with underscores replaced by dashes. + :param cls: the command class to instantiate. This defaults to + :class:`Command`. + + .. 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.Optional[t.Callable[..., t.Any]] = 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 = Command + + def decorator(f: t.Callable[..., t.Any]) -> Command: + 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__ + + cmd = cls( # type: ignore[misc] + name=name or f.__name__.lower().replace("_", "-"), # type: ignore[arg-type] + callback=f, + params=params, + **attrs, + ) + cmd.__doc__ = f.__doc__ + return cmd + + if func is not None: + return decorator(func) + + return decorator + + +@t.overload +def group( + __func: t.Callable[..., t.Any], +) -> Group: + ... + + +@t.overload +def group( + name: t.Optional[str] = None, + **attrs: t.Any, +) -> t.Callable[[F], Group]: + ... + + +def group( + name: t.Union[str, t.Callable[..., t.Any], None] = None, **attrs: t.Any +) -> t.Union[Group, t.Callable[[F], Group]]: + """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 attrs.get("cls") is None: + attrs["cls"] = Group + + if callable(name): + grp: t.Callable[[F], Group] = t.cast(Group, command(**attrs)) + return grp(name) + + return t.cast(Group, command(name, **attrs)) + + +def _param_memo(f: FC, 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, **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. + + :param cls: the argument class to instantiate. This defaults to + :class:`Argument`. + """ + + def decorator(f: FC) -> FC: + ArgumentClass = attrs.pop("cls", None) or Argument + _param_memo(f, ArgumentClass(param_decls, **attrs)) + return f + + return decorator + + +def option(*param_decls: str, **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. + + :param cls: the option class to instantiate. This defaults to + :class:`Option`. + """ + + def decorator(f: FC) -> FC: + # Issue 926, copy attrs, so pre-defined options can re-use the same cls= + option_attrs = attrs.copy() + OptionClass = option_attrs.pop("cls", None) or Option + _param_memo(f, OptionClass(param_decls, **option_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: t.Optional[str] = None, + *param_decls: str, + package_name: t.Optional[str] = None, + prog_name: t.Optional[str] = None, + message: t.Optional[str] = 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``. On Python < 3.8, the ``importlib_metadata`` + backport must be installed. + + 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: + metadata: t.Optional[types.ModuleType] + + try: + from importlib import metadata # type: ignore + except ImportError: + # Python < 3.8 + import importlib_metadata as metadata # type: ignore + + try: + version = metadata.version(package_name) # type: ignore + except metadata.PackageNotFoundError: # type: ignore + 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( + t.cast(str, 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]: + """Add a ``--help`` option which immediately prints the help page + and exits the program. + + This is usually unnecessary, as the ``--help`` option is added to + each command automatically unless ``add_help_option=False`` is + passed. + + :param param_decls: One or more option names. Defaults to the single + value ``"--help"``. + :param kwargs: Extra arguments are passed to :func:`option`. + """ + + def callback(ctx: Context, param: Parameter, value: bool) -> None: + if not value or ctx.resilient_parsing: + return + + 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["callback"] = callback + return option(*param_decls, **kwargs) diff --git a/venv/lib/python3.10/site-packages/click/exceptions.py b/venv/lib/python3.10/site-packages/click/exceptions.py new file mode 100644 index 000000000..9e20b3eb5 --- /dev/null +++ b/venv/lib/python3.10/site-packages/click/exceptions.py @@ -0,0 +1,287 @@ +import os +import typing as t +from gettext import gettext as _ +from gettext import ngettext + +from ._compat import get_text_stderr +from .utils import echo + +if t.TYPE_CHECKING: + from .core import Context + from .core import Parameter + + +def _join_param_hints( + param_hint: t.Optional[t.Union[t.Sequence[str], str]] +) -> t.Optional[str]: + 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) + self.message = message + + def format_message(self) -> str: + return self.message + + def __str__(self) -> str: + return self.message + + def show(self, file: t.Optional[t.IO] = None) -> None: + if file is None: + file = get_text_stderr() + + echo(_("Error: {message}").format(message=self.format_message()), file=file) + + +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: t.Optional["Context"] = None) -> None: + super().__init__(message) + self.ctx = ctx + self.cmd = self.ctx.command if self.ctx else None + + def show(self, file: t.Optional[t.IO] = 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: t.Optional["Context"] = None, + param: t.Optional["Parameter"] = None, + param_hint: t.Optional[str] = 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: t.Optional[str] = None, + ctx: t.Optional["Context"] = None, + param: t.Optional["Parameter"] = None, + param_hint: t.Optional[str] = None, + param_type: t.Optional[str] = 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: t.Optional[str] = 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(self.param) + 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: t.Optional[str] = None, + possibilities: t.Optional[t.Sequence[str]] = None, + ctx: t.Optional["Context"] = 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: t.Optional["Context"] = 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 FileError(ClickException): + """Raised if a file cannot be opened.""" + + def __init__(self, filename: str, hint: t.Optional[str] = None) -> None: + if hint is None: + hint = _("unknown error") + + super().__init__(hint) + self.ui_filename = os.fsdecode(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 = code diff --git a/venv/lib/python3.10/site-packages/click/formatting.py b/venv/lib/python3.10/site-packages/click/formatting.py new file mode 100644 index 000000000..ddd2a2f82 --- /dev/null +++ b/venv/lib/python3.10/site-packages/click/formatting.py @@ -0,0 +1,301 @@ +import typing as t +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: t.Optional[int] = None + + +def measure_table(rows: t.Iterable[t.Tuple[str, str]]) -> t.Tuple[int, ...]: + widths: t.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: t.Iterable[t.Tuple[str, str]], col_count: int +) -> t.Iterator[t.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: t.List[t.Tuple[int, bool, str]] = [] + buf: t.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: t.Optional[int] = None, + max_width: t.Optional[int] = 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 = 0 + self.buffer: t.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: t.Optional[str] = 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: t.Sequence[t.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) -> t.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) -> t.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: t.Sequence[str]) -> t.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.10/site-packages/click/globals.py b/venv/lib/python3.10/site-packages/click/globals.py new file mode 100644 index 000000000..480058f10 --- /dev/null +++ b/venv/lib/python3.10/site-packages/click/globals.py @@ -0,0 +1,68 @@ +import typing as t +from threading import local + +if t.TYPE_CHECKING: + import typing_extensions as te + from .core import Context + +_local = local() + + +@t.overload +def get_current_context(silent: "te.Literal[False]" = False) -> "Context": + ... + + +@t.overload +def get_current_context(silent: bool = ...) -> t.Optional["Context"]: + ... + + +def get_current_context(silent: bool = False) -> t.Optional["Context"]: + """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: t.Optional[bool] = None) -> t.Optional[bool]: + """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.10/site-packages/click/parser.py b/venv/lib/python3.10/site-packages/click/parser.py new file mode 100644 index 000000000..2d5a2ed7b --- /dev/null +++ b/venv/lib/python3.10/site-packages/click/parser.py @@ -0,0 +1,529 @@ +""" +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 +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: + import typing_extensions as te + 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: t.Sequence[str], nargs_spec: t.Sequence[int] +) -> t.Tuple[t.Sequence[t.Union[str, t.Sequence[t.Optional[str]], None]], t.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: t.List[t.Union[str, t.Tuple[t.Optional[str], ...], None]] = [] + spos: t.Optional[int] = None + + def _fetch(c: "te.Deque[V]") -> t.Optional[V]: + 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) -> t.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: t.Optional["Context"]) -> 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)}" + + +def split_arg_string(string: str) -> t.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. + """ + 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 + + +class Option: + def __init__( + self, + obj: "CoreOption", + opts: t.Sequence[str], + dest: t.Optional[str], + action: t.Optional[str] = None, + nargs: int = 1, + const: t.Optional[t.Any] = None, + ): + self._short_opts = [] + self._long_opts = [] + self.prefixes = 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: str, 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: t.Optional[str], nargs: int = 1): + self.dest = dest + self.nargs = nargs + self.obj = obj + + def process( + self, + value: t.Union[t.Optional[str], t.Sequence[t.Optional[str]]], + 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: t.List[str]) -> None: + self.opts: t.Dict[str, t.Any] = {} + self.largs: t.List[str] = [] + self.rargs = rargs + self.order: t.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. + """ + + def __init__(self, ctx: t.Optional["Context"] = 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 = 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 = 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: t.Dict[str, Option] = {} + self._long_opt: t.Dict[str, Option] = {} + self._opt_prefixes = {"-", "--"} + self._args: t.List[Argument] = [] + + def add_option( + self, + obj: "CoreOption", + opts: t.Sequence[str], + dest: t.Optional[str], + action: t.Optional[str] = None, + nargs: int = 1, + const: t.Optional[t.Any] = 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: t.Optional[str], 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: t.List[str] + ) -> t.Tuple[t.Dict[str, t.Any], t.List[str], t.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: t.Optional[str], 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 re-combinate 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) diff --git a/venv/lib/python3.10/site-packages/click/py.typed b/venv/lib/python3.10/site-packages/click/py.typed new file mode 100644 index 000000000..e69de29bb diff --git a/venv/lib/python3.10/site-packages/click/shell_completion.py b/venv/lib/python3.10/site-packages/click/shell_completion.py new file mode 100644 index 000000000..c17a8e643 --- /dev/null +++ b/venv/lib/python3.10/site-packages/click/shell_completion.py @@ -0,0 +1,580 @@ +import os +import re +import typing as t +from gettext import gettext as _ + +from .core import Argument +from .core import BaseCommand +from .core import Context +from .core import MultiCommand +from .core import Option +from .core import Parameter +from .core import ParameterSource +from .parser import split_arg_string +from .utils import echo + + +def shell_complete( + cli: BaseCommand, + ctx_args: t.Dict[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: t.Optional[str] = None, + **kwargs: t.Any, + ) -> None: + self.value = value + self.type = type + self.help = 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 +} + +compdef %(complete_func)s %(prog_name)s; +""" + +_SOURCE_FISH = """\ +function %(complete_func)s; + set -l response; + + for value in (env %(complete_var)s=fish_complete COMP_WORDS=(commandline -cp) \ +COMP_CWORD=(commandline -t) %(prog_name)s); + set response $response $value; + end; + + 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: BaseCommand, + ctx_args: t.Dict[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("-", "_"), re.ASCII) + return f"_{safe_name}_completion" + + def source_vars(self) -> t.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) -> t.Tuple[t.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: t.List[str], incomplete: str + ) -> t.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 + + def _check_version(self) -> None: + import subprocess + + output = subprocess.run( + ["bash", "-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": + raise RuntimeError( + _( + "Shell completion is not supported for Bash" + " versions older than 4.4." + ) + ) + else: + raise RuntimeError( + _("Couldn't detect Bash version, shell completion is not supported.") + ) + + def source(self) -> str: + self._check_version() + return super().source() + + def get_completion_args(self) -> t.Tuple[t.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) -> t.Tuple[t.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) -> t.Tuple[t.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}" + + +_available_shells: t.Dict[str, t.Type[ShellComplete]] = { + "bash": BashComplete, + "fish": FishComplete, + "zsh": ZshComplete, +} + + +def add_completion_class( + cls: t.Type[ShellComplete], name: t.Optional[str] = None +) -> None: + """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 + + +def get_completion_class(shell: str) -> t.Optional[t.Type[ShellComplete]]: + """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 _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 + value = ctx.params[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: t.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: BaseCommand, ctx_args: t.Dict[str, t.Any], prog_name: str, args: t.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 + ctx = cli.make_context(prog_name, args.copy(), **ctx_args) + args = ctx.protected_args + ctx.args + + while args: + command = ctx.command + + if isinstance(command, MultiCommand): + if not command.chain: + name, cmd, args = command.resolve_command(ctx, args) + + if cmd is None: + return ctx + + ctx = cmd.make_context(name, args, parent=ctx, resilient_parsing=True) + args = ctx.protected_args + ctx.args + else: + while args: + name, cmd, args = command.resolve_command(ctx, args) + + if cmd is None: + return ctx + + sub_ctx = cmd.make_context( + name, + args, + parent=ctx, + allow_extra_args=True, + allow_interspersed_args=False, + resilient_parsing=True, + ) + 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: t.List[str], incomplete: str +) -> t.Tuple[t.Union[BaseCommand, 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.10/site-packages/click/termui.py b/venv/lib/python3.10/site-packages/click/termui.py new file mode 100644 index 000000000..bfb2f5ae6 --- /dev/null +++ b/venv/lib/python3.10/site-packages/click/termui.py @@ -0,0 +1,787 @@ +import inspect +import io +import itertools +import os +import sys +import typing as t +from gettext import gettext as _ + +from ._compat import isatty +from ._compat import strip_ansi +from ._compat import WIN +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.Optional[t.Any] = None, + show_choices: bool = True, + type: t.Optional[ParamType] = 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 # type: ignore + + return default + + +def prompt( + text: str, + default: t.Optional[t.Any] = None, + hide_input: bool = False, + confirmation_prompt: t.Union[bool, str] = False, + type: t.Optional[t.Union[ParamType, t.Any]] = None, + value_proc: t.Optional[t.Callable[[str], t.Any]] = 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) # noqa: B306 + 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: t.Optional[bool] = 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: t.Union[t.Iterable[str], t.Callable[[], t.Iterable[str]], str], + color: t.Optional[bool] = 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[[], t.Iterable[str]], text_or_generator)() + elif isinstance(text_or_generator, str): + i = [text_or_generator] + else: + i = iter(t.cast(t.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) + + +def progressbar( + iterable: t.Optional[t.Iterable[V]] = None, + length: t.Optional[int] = None, + label: t.Optional[str] = None, + show_eta: bool = True, + show_percent: t.Optional[bool] = None, + show_pos: bool = False, + item_show_func: t.Optional[t.Callable[[t.Optional[V]], t.Optional[str]]] = None, + fill_char: str = "#", + empty_char: str = "-", + bar_template: str = "%(label)s [%(bar)s] %(info)s", + info_sep: str = " ", + width: int = 36, + file: t.Optional[t.TextIO] = None, + color: t.Optional[bool] = 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 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. + + .. 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 + Added the ``update_min_steps`` parameter. + + .. versionchanged:: 4.0 + Added the ``color`` parameter. Added the ``update`` method to + the object. + + .. versionadded:: 2.0 + """ + from ._termui_impl import ProgressBar + + color = resolve_color_default(color) + return ProgressBar( + iterable=iterable, + length=length, + 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 + if WIN: + os.system("cls") + else: + sys.stdout.write("\033[2J\033[1;1H") + + +def _interpret_color( + color: t.Union[int, t.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: t.Optional[t.Union[int, t.Tuple[int, int, int], str]] = None, + bg: t.Optional[t.Union[int, t.Tuple[int, int, int], str]] = None, + bold: t.Optional[bool] = None, + dim: t.Optional[bool] = None, + underline: t.Optional[bool] = None, + overline: t.Optional[bool] = None, + italic: t.Optional[bool] = None, + blink: t.Optional[bool] = None, + reverse: t.Optional[bool] = None, + strikethrough: t.Optional[bool] = 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.Optional[t.Any] = None, + file: t.Optional[t.IO[t.AnyStr]] = None, + nl: bool = True, + err: bool = False, + color: t.Optional[bool] = 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) + + +def edit( + text: t.Optional[t.AnyStr] = None, + editor: t.Optional[str] = None, + env: t.Optional[t.Mapping[str, str]] = None, + require_save: bool = True, + extension: str = ".txt", + filename: t.Optional[str] = None, +) -> t.Optional[t.AnyStr]: + 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. + """ + 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) + + ed.edit_file(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.Optional[t.Callable[[bool], str]] = 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() -> t.ContextManager[int]: + from ._termui_impl import raw_terminal as f + + return f() + + +def pause(info: t.Optional[str] = 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.10/site-packages/click/testing.py b/venv/lib/python3.10/site-packages/click/testing.py new file mode 100644 index 000000000..e395c2edf --- /dev/null +++ b/venv/lib/python3.10/site-packages/click/testing.py @@ -0,0 +1,479 @@ +import contextlib +import io +import os +import shlex +import shutil +import sys +import tempfile +import typing as t +from types import TracebackType + +from . import formatting +from . import termui +from . import utils +from ._compat import _find_binary_reader + +if t.TYPE_CHECKING: + from .core import BaseCommand + + +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) -> t.List[bytes]: + return [self._echo(x) for x in self._input.readlines()] + + def __iter__(self) -> t.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: t.Optional[EchoingStdin]) -> t.Iterator[None]: + if stream is None: + yield + else: + stream._paused = True + yield + stream._paused = False + + +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 make_input_stream( + input: t.Optional[t.Union[str, bytes, t.IO]], charset: str +) -> t.BinaryIO: + # Is already an input stream. + if hasattr(input, "read"): + rv = _find_binary_reader(t.cast(t.IO, 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(t.cast(bytes, input)) + + +class Result: + """Holds the captured result of an invoked CLI script.""" + + def __init__( + self, + runner: "CliRunner", + stdout_bytes: bytes, + stderr_bytes: t.Optional[bytes], + return_value: t.Any, + exit_code: int, + exception: t.Optional[BaseException], + exc_info: t.Optional[ + t.Tuple[t.Type[BaseException], BaseException, TracebackType] + ] = None, + ): + #: The runner that created the result + self.runner = runner + #: The standard output as bytes. + self.stdout_bytes = stdout_bytes + #: The standard error as bytes, or None if not available + self.stderr_bytes = stderr_bytes + #: The value returned from the invoked command. + #: + #: .. versionadded:: 8.0 + self.return_value = return_value + #: The exit code as integer. + self.exit_code = exit_code + #: The exception that happened if one did. + self.exception = exception + #: The traceback + self.exc_info = exc_info + + @property + def output(self) -> str: + """The (standard) output as unicode string.""" + return self.stdout + + @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.""" + if self.stderr_bytes is None: + raise ValueError("stderr not separately captured") + 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 stdin writes + to stdout. This is useful for showing examples in + some circumstances. Note that regular prompts + will automatically echo the input. + :param mix_stderr: if this is set to `False`, then stdout and stderr are + preserved as independent streams. This is useful for + Unix-philosophy apps that have predictable stdout and + noisy stderr, such that each may be measured + independently + """ + + def __init__( + self, + charset: str = "utf-8", + env: t.Optional[t.Mapping[str, t.Optional[str]]] = None, + echo_stdin: bool = False, + mix_stderr: bool = True, + ) -> None: + self.charset = charset + self.env = env or {} + self.echo_stdin = echo_stdin + self.mix_stderr = mix_stderr + + def get_default_prog_name(self, cli: "BaseCommand") -> 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: t.Optional[t.Mapping[str, t.Optional[str]]] = None + ) -> t.Mapping[str, t.Optional[str]]: + """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: t.Optional[t.Union[str, bytes, t.IO]] = None, + env: t.Optional[t.Mapping[str, t.Optional[str]]] = None, + color: bool = False, + ) -> t.Iterator[t.Tuple[io.BytesIO, t.Optional[io.BytesIO]]]: + """A context manager that sets up the isolation for invoking of a + command line tool. This sets up stdin 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. + + .. versionchanged:: 8.0 + ``stderr`` 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) + + bytes_output = io.BytesIO() + + if self.echo_stdin: + bytes_input = echo_input = t.cast( + t.BinaryIO, EchoingStdin(bytes_input, bytes_output) + ) + + 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( + bytes_output, encoding=self.charset, name="", mode="w" + ) + + bytes_error = None + if self.mix_stderr: + sys.stderr = sys.stdout + else: + bytes_error = io.BytesIO() + sys.stderr = _NamedTextIOWrapper( + bytes_error, + encoding=self.charset, + name="", + mode="w", + errors="backslashreplace", + ) + + @_pause_echo(echo_input) # type: ignore + def visible_input(prompt: t.Optional[str] = None) -> str: + sys.stdout.write(prompt or "") + val = text_input.readline().rstrip("\r\n") + sys.stdout.write(f"{val}\n") + sys.stdout.flush() + return val + + @_pause_echo(echo_input) # type: ignore + def hidden_input(prompt: t.Optional[str] = None) -> str: + sys.stdout.write(f"{prompt or ''}\n") + sys.stdout.flush() + return text_input.readline().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.Optional[t.IO] = None, color: t.Optional[bool] = 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 + termui.visible_prompt_func = visible_input + termui.hidden_prompt_func = hidden_input + termui._getchar = _getchar + utils.should_strip_ansi = should_strip_ansi # type: ignore + + 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 (bytes_output, bytes_error) + 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 + formatting.FORCED_WIDTH = old_forced_width + + def invoke( + self, + cli: "BaseCommand", + args: t.Optional[t.Union[str, t.Sequence[str]]] = None, + input: t.Optional[t.Union[str, bytes, t.IO]] = None, + env: t.Optional[t.Mapping[str, t.Optional[str]]] = None, + catch_exceptions: bool = True, + 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``. + :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. + + .. 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 + with self.isolation(input=input, env=env, color=color) as outstreams: + return_value = None + exception: t.Optional[BaseException] = 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(t.Optional[t.Union[int, t.Any]], 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() + stdout = outstreams[0].getvalue() + if self.mix_stderr: + stderr = None + else: + stderr = outstreams[1].getvalue() # type: ignore + + return Result( + runner=self, + stdout_bytes=stdout, + stderr_bytes=stderr, + return_value=return_value, + exit_code=exit_code, + exception=exception, + exc_info=exc_info, # type: ignore + ) + + @contextlib.contextmanager + def isolated_filesystem( + self, temp_dir: t.Optional[t.Union[str, os.PathLike]] = None + ) -> t.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) # type: ignore[type-var] + os.chdir(dt) + + try: + yield t.cast(str, dt) + finally: + os.chdir(cwd) + + if temp_dir is None: + try: + shutil.rmtree(dt) + except OSError: # noqa: B014 + pass diff --git a/venv/lib/python3.10/site-packages/click/types.py b/venv/lib/python3.10/site-packages/click/types.py new file mode 100644 index 000000000..b45ee53d0 --- /dev/null +++ b/venv/lib/python3.10/site-packages/click/types.py @@ -0,0 +1,1073 @@ +import os +import stat +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 get_filesystem_encoding +from ._compat import open_stream +from .exceptions import BadParameter +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 + + +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[t.Optional[str]] = None + + def to_info_dict(self) -> t.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: t.Optional["Parameter"] = None, + ctx: t.Optional["Context"] = None, + ) -> t.Any: + if value is not None: + return self.convert(value, param, ctx) + + def get_metavar(self, param: "Parameter") -> t.Optional[str]: + """Returns the metavar default for this param if it provides one.""" + + def get_missing_message(self, param: "Parameter") -> t.Optional[str]: + """Optionally might return extra information about a missing + parameter. + + .. versionadded:: 2.0 + """ + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> 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) -> t.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: t.Optional["Parameter"] = None, + ctx: t.Optional["Context"] = 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 + ) -> t.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 = func.__name__ + self.func = func + + def to_info_dict(self) -> t.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: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> 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: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + return value + + def __repr__(self) -> str: + return "UNPROCESSED" + + +class StringParamType(ParamType): + name = "text" + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + if isinstance(value, bytes): + enc = _get_argv_encoding() + try: + value = value.decode(enc) + except UnicodeError: + fs_enc = get_filesystem_encoding() + 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): + """The choice type allows a value to be checked against a fixed set + of supported values. All of these values have to be strings. + + You should only pass a list or tuple of choices. Other iterables + (like generators) may lead to surprising results. + + The resulting value will always be one of the originally passed choices + regardless of ``case_sensitive`` or any ``ctx.token_normalize_func`` + being specified. + + See :ref:`choice-opts` for an example. + + :param case_sensitive: Set to false to make choices case + insensitive. Defaults to true. + """ + + name = "choice" + + def __init__(self, choices: t.Sequence[str], case_sensitive: bool = True) -> None: + self.choices = choices + self.case_sensitive = case_sensitive + + def to_info_dict(self) -> t.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 get_metavar(self, param: "Parameter") -> str: + choices_str = "|".join(self.choices) + + # 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") -> str: + return _("Choose from:\n\t{choices}").format(choices=",\n\t".join(self.choices)) + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + # Match through normalization and case sensitivity + # first do token_normalize_func, then lowercase + # preserve original `value` to produce an accurate message in + # `self.fail` + normed_value = value + normed_choices = {choice: choice for choice in self.choices} + + if ctx is not None and ctx.token_normalize_func is not None: + normed_value = ctx.token_normalize_func(value) + normed_choices = { + ctx.token_normalize_func(normed_choice): original + for normed_choice, original in normed_choices.items() + } + + if not self.case_sensitive: + normed_value = normed_value.casefold() + normed_choices = { + normed_choice.casefold(): original + for normed_choice, original in normed_choices.items() + } + + if normed_value in normed_choices: + return normed_choices[normed_value] + + choices_str = ", ".join(map(repr, self.choices)) + self.fail( + 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), + param, + ctx, + ) + + def __repr__(self) -> str: + return f"Choice({list(self.choices)})" + + def shell_complete( + self, ctx: "Context", param: "Parameter", incomplete: str + ) -> t.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: t.Optional[t.Sequence[str]] = None): + self.formats = formats or ["%Y-%m-%d", "%Y-%m-%dT%H:%M:%S", "%Y-%m-%d %H:%M:%S"] + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict["formats"] = self.formats + return info_dict + + def get_metavar(self, param: "Parameter") -> str: + return f"[{'|'.join(self.formats)}]" + + def _try_to_convert_date(self, value: t.Any, format: str) -> t.Optional[datetime]: + try: + return datetime.strptime(value, format) + except ValueError: + return None + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> 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[t.Type] + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> 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: t.Optional[float] = None, + max: t.Optional[float] = 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) -> t.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: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> 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: "te.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: "te.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: t.Optional[float] = None, + max: t.Optional[float] = 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: "te.Literal[1, -1]", open: bool) -> float: + if not open: + return bound + + # Could use Python 3.9's 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: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> 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: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> 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. + + Starting with Click 2.0, 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. + """ + + name = "filename" + envvar_list_splitter = os.path.pathsep + + def __init__( + self, + mode: str = "r", + encoding: t.Optional[str] = None, + errors: t.Optional[str] = "strict", + lazy: t.Optional[bool] = 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) -> t.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: t.Any) -> bool: + if self.lazy is not None: + return self.lazy + if value == "-": + return False + elif "w" in self.mode: + return True + return False + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + try: + if hasattr(value, "read") or hasattr(value, "write"): + return value + + lazy = self.resolve_lazy_flag(value) + + if lazy: + f: t.IO = t.cast( + t.IO, + LazyFile( + value, self.mode, self.encoding, self.errors, atomic=self.atomic + ), + ) + + if ctx is not None: + ctx.call_on_close(f.close_intelligently) # type: ignore + + return f + + 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: # noqa: B014 + self.fail(f"'{os.fsdecode(value)}': {e.strerror}", param, ctx) + + def shell_complete( + self, ctx: "Context", param: "Parameter", incomplete: str + ) -> t.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")] + + +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 ``type=pathlib.Path``. + + .. versionchanged:: 6.0 + Added the ``allow_dash`` parameter. + """ + + envvar_list_splitter = 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: t.Optional[t.Type] = 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 = _("file") + elif self.dir_okay and not self.file_okay: + self.name = _("directory") + else: + self.name = _("path") + + def to_info_dict(self) -> t.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, rv: t.Any) -> t.Any: + if self.type is not None and not isinstance(rv, self.type): + if self.type is str: + rv = os.fsdecode(rv) + elif self.type is bytes: + rv = os.fsencode(rv) + else: + rv = self.type(rv) + + return rv + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + rv = value + + is_dash = self.file_okay and self.allow_dash and rv in (b"-", "-") + + if not is_dash: + if self.resolve_path: + # os.path.realpath doesn't resolve symlinks on Windows + # until Python 3.8. Use pathlib for now. + import pathlib + + rv = os.fsdecode(pathlib.Path(rv).resolve()) + + 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=os.fsdecode(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=os.fsdecode(value) + ), + param, + ctx, + ) + if not self.dir_okay and stat.S_ISDIR(st.st_mode): + self.fail( + _("{name} '{filename}' is a directory.").format( + name=self.name.title(), filename=os.fsdecode(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=os.fsdecode(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=os.fsdecode(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=os.fsdecode(value) + ), + param, + ctx, + ) + + return self.coerce_path_result(rv) + + def shell_complete( + self, ctx: "Context", param: "Parameter", incomplete: str + ) -> t.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: t.Sequence[t.Union[t.Type, ParamType]]) -> None: + self.types = [convert_type(ty) for ty in types] + + def to_info_dict(self) -> t.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: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> 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)) + + +def convert_type(ty: t.Optional[t.Any], default: t.Optional[t.Any] = 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() diff --git a/venv/lib/python3.10/site-packages/click/utils.py b/venv/lib/python3.10/site-packages/click/utils.py new file mode 100644 index 000000000..8283788ac --- /dev/null +++ b/venv/lib/python3.10/site-packages/click/utils.py @@ -0,0 +1,580 @@ +import os +import re +import sys +import typing as t +from functools import update_wrapper +from types import ModuleType + +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 get_filesystem_encoding +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 + +F = t.TypeVar("F", bound=t.Callable[..., t.Any]) + + +def _posixify(name: str) -> str: + return "-".join(name.split()).lower() + + +def safecall(func: F) -> F: + """Wraps a function so that it swallows exceptions.""" + + def wrapper(*args, **kwargs): # type: ignore + try: + return func(*args, **kwargs) + except Exception: + pass + + return update_wrapper(t.cast(F, wrapper), func) + + +def make_str(value: t.Any) -> str: + """Converts a value into a valid string.""" + if isinstance(value, bytes): + try: + return value.decode(get_filesystem_encoding()) + 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, + mode: str = "r", + encoding: t.Optional[str] = None, + errors: t.Optional[str] = "strict", + atomic: bool = False, + ): + self.name = filename + self.mode = mode + self.encoding = encoding + self.errors = errors + self.atomic = atomic + self._f: t.Optional[t.IO] + + if filename == "-": + 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: + """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: # noqa: E402 + 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, exc_value, tb): # type: ignore + self.close_intelligently() + + def __iter__(self) -> t.Iterator[t.AnyStr]: + self.open() + return iter(self._f) # type: ignore + + +class KeepOpenFile: + def __init__(self, file: t.IO) -> None: + self._file = file + + def __getattr__(self, name: str) -> t.Any: + return getattr(self._file, name) + + def __enter__(self) -> "KeepOpenFile": + return self + + def __exit__(self, exc_type, exc_value, tb): # type: ignore + pass + + def __repr__(self) -> str: + return repr(self._file) + + def __iter__(self) -> t.Iterator[t.AnyStr]: + return iter(self._file) + + +def echo( + message: t.Optional[t.Any] = None, + file: t.Optional[t.IO[t.Any]] = None, + nl: bool = True, + err: bool = False, + color: t.Optional[bool] = 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() + + # Convert non bytes/text into the native string type. + if message is not None and not isinstance(message, (str, bytes, bytearray)): + out: t.Optional[t.Union[str, bytes]] = 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) # type: ignore + elif not color: + out = strip_ansi(out) + + file.write(out) # type: ignore + file.flush() + + +def get_binary_stream(name: "te.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: "te.Literal['stdin', 'stdout', 'stderr']", + encoding: t.Optional[str] = None, + errors: t.Optional[str] = "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, + mode: str = "r", + encoding: t.Optional[str] = None, + errors: t.Optional[str] = "strict", + lazy: bool = False, + atomic: bool = False, +) -> t.IO: + """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 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, 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, KeepOpenFile(f)) + + return f + + +def format_filename( + filename: t.Union[str, bytes, os.PathLike], shorten: bool = False +) -> str: + """Formats a filename for user display. The main purpose of this + function is to ensure that the filename can be displayed at all. This + will decode the filename to unicode if necessary in a way that it will + not fail. Optionally, it can shorten the filename to not include the + full path to the filename. + + :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) + + return os.fsdecode(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 affect 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) -> 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: t.Optional[str] = None, _main: t.Optional[ModuleType] = 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. + if getattr(_main, "__package__", None) is 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: t.Iterable[str], + *, + user: bool = True, + env: bool = True, + glob_recursive: bool = True, +) -> t.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.10/site-packages/colorama-0.4.6.dist-info/INSTALLER b/venv/lib/python3.10/site-packages/colorama-0.4.6.dist-info/INSTALLER new file mode 100644 index 000000000..a1b589e38 --- /dev/null +++ b/venv/lib/python3.10/site-packages/colorama-0.4.6.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.10/site-packages/colorama-0.4.6.dist-info/METADATA b/venv/lib/python3.10/site-packages/colorama-0.4.6.dist-info/METADATA new file mode 100644 index 000000000..a1b5c5754 --- /dev/null +++ b/venv/lib/python3.10/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.10/site-packages/colorama-0.4.6.dist-info/RECORD b/venv/lib/python3.10/site-packages/colorama-0.4.6.dist-info/RECORD new file mode 100644 index 000000000..687d244ec --- /dev/null +++ b/venv/lib/python3.10/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-310.pyc,, +colorama/__pycache__/ansi.cpython-310.pyc,, +colorama/__pycache__/ansitowin32.cpython-310.pyc,, +colorama/__pycache__/initialise.cpython-310.pyc,, +colorama/__pycache__/win32.cpython-310.pyc,, +colorama/__pycache__/winterm.cpython-310.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-310.pyc,, +colorama/tests/__pycache__/ansi_test.cpython-310.pyc,, +colorama/tests/__pycache__/ansitowin32_test.cpython-310.pyc,, +colorama/tests/__pycache__/initialise_test.cpython-310.pyc,, +colorama/tests/__pycache__/isatty_test.cpython-310.pyc,, +colorama/tests/__pycache__/utils.cpython-310.pyc,, +colorama/tests/__pycache__/winterm_test.cpython-310.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.10/site-packages/colorama-0.4.6.dist-info/WHEEL b/venv/lib/python3.10/site-packages/colorama-0.4.6.dist-info/WHEEL new file mode 100644 index 000000000..d79189fda --- /dev/null +++ b/venv/lib/python3.10/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.10/site-packages/colorama-0.4.6.dist-info/licenses/LICENSE.txt b/venv/lib/python3.10/site-packages/colorama-0.4.6.dist-info/licenses/LICENSE.txt new file mode 100644 index 000000000..3105888ec --- /dev/null +++ b/venv/lib/python3.10/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.10/site-packages/colorama/__init__.py b/venv/lib/python3.10/site-packages/colorama/__init__.py new file mode 100644 index 000000000..383101cdb --- /dev/null +++ b/venv/lib/python3.10/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.10/site-packages/colorama/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/colorama/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 000000000..fa086e6b5 Binary files /dev/null and b/venv/lib/python3.10/site-packages/colorama/__pycache__/__init__.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/colorama/__pycache__/ansi.cpython-310.pyc b/venv/lib/python3.10/site-packages/colorama/__pycache__/ansi.cpython-310.pyc new file mode 100644 index 000000000..fe2ce02f0 Binary files /dev/null and b/venv/lib/python3.10/site-packages/colorama/__pycache__/ansi.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/colorama/__pycache__/ansitowin32.cpython-310.pyc b/venv/lib/python3.10/site-packages/colorama/__pycache__/ansitowin32.cpython-310.pyc new file mode 100644 index 000000000..11abdb14c Binary files /dev/null and b/venv/lib/python3.10/site-packages/colorama/__pycache__/ansitowin32.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/colorama/__pycache__/initialise.cpython-310.pyc b/venv/lib/python3.10/site-packages/colorama/__pycache__/initialise.cpython-310.pyc new file mode 100644 index 000000000..ff3a37471 Binary files /dev/null and b/venv/lib/python3.10/site-packages/colorama/__pycache__/initialise.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/colorama/__pycache__/win32.cpython-310.pyc b/venv/lib/python3.10/site-packages/colorama/__pycache__/win32.cpython-310.pyc new file mode 100644 index 000000000..b8f4e2fbf Binary files /dev/null and b/venv/lib/python3.10/site-packages/colorama/__pycache__/win32.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/colorama/__pycache__/winterm.cpython-310.pyc b/venv/lib/python3.10/site-packages/colorama/__pycache__/winterm.cpython-310.pyc new file mode 100644 index 000000000..d6a8a9166 Binary files /dev/null and b/venv/lib/python3.10/site-packages/colorama/__pycache__/winterm.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/colorama/ansi.py b/venv/lib/python3.10/site-packages/colorama/ansi.py new file mode 100644 index 000000000..11ec695ff --- /dev/null +++ b/venv/lib/python3.10/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.10/site-packages/colorama/ansitowin32.py b/venv/lib/python3.10/site-packages/colorama/ansitowin32.py new file mode 100644 index 000000000..abf209e60 --- /dev/null +++ b/venv/lib/python3.10/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.10/site-packages/colorama/initialise.py b/venv/lib/python3.10/site-packages/colorama/initialise.py new file mode 100644 index 000000000..d5fd4b71f --- /dev/null +++ b/venv/lib/python3.10/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.10/site-packages/colorama/tests/__init__.py b/venv/lib/python3.10/site-packages/colorama/tests/__init__.py new file mode 100644 index 000000000..8c5661e93 --- /dev/null +++ b/venv/lib/python3.10/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.10/site-packages/colorama/tests/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/colorama/tests/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 000000000..7e4d1d39c Binary files /dev/null and b/venv/lib/python3.10/site-packages/colorama/tests/__pycache__/__init__.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/colorama/tests/__pycache__/ansi_test.cpython-310.pyc b/venv/lib/python3.10/site-packages/colorama/tests/__pycache__/ansi_test.cpython-310.pyc new file mode 100644 index 000000000..7cea850b1 Binary files /dev/null and b/venv/lib/python3.10/site-packages/colorama/tests/__pycache__/ansi_test.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/colorama/tests/__pycache__/ansitowin32_test.cpython-310.pyc b/venv/lib/python3.10/site-packages/colorama/tests/__pycache__/ansitowin32_test.cpython-310.pyc new file mode 100644 index 000000000..886c1a65f Binary files /dev/null and b/venv/lib/python3.10/site-packages/colorama/tests/__pycache__/ansitowin32_test.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/colorama/tests/__pycache__/initialise_test.cpython-310.pyc b/venv/lib/python3.10/site-packages/colorama/tests/__pycache__/initialise_test.cpython-310.pyc new file mode 100644 index 000000000..fc915ebfc Binary files /dev/null and b/venv/lib/python3.10/site-packages/colorama/tests/__pycache__/initialise_test.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/colorama/tests/__pycache__/isatty_test.cpython-310.pyc b/venv/lib/python3.10/site-packages/colorama/tests/__pycache__/isatty_test.cpython-310.pyc new file mode 100644 index 000000000..6c87f2aa4 Binary files /dev/null and b/venv/lib/python3.10/site-packages/colorama/tests/__pycache__/isatty_test.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/colorama/tests/__pycache__/utils.cpython-310.pyc b/venv/lib/python3.10/site-packages/colorama/tests/__pycache__/utils.cpython-310.pyc new file mode 100644 index 000000000..d3a33b9df Binary files /dev/null and b/venv/lib/python3.10/site-packages/colorama/tests/__pycache__/utils.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/colorama/tests/__pycache__/winterm_test.cpython-310.pyc b/venv/lib/python3.10/site-packages/colorama/tests/__pycache__/winterm_test.cpython-310.pyc new file mode 100644 index 000000000..a8bd6748f Binary files /dev/null and b/venv/lib/python3.10/site-packages/colorama/tests/__pycache__/winterm_test.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/colorama/tests/ansi_test.py b/venv/lib/python3.10/site-packages/colorama/tests/ansi_test.py new file mode 100644 index 000000000..0a20c80f8 --- /dev/null +++ b/venv/lib/python3.10/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.10/site-packages/colorama/tests/ansitowin32_test.py b/venv/lib/python3.10/site-packages/colorama/tests/ansitowin32_test.py new file mode 100644 index 000000000..91ca551f9 --- /dev/null +++ b/venv/lib/python3.10/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.10/site-packages/colorama/tests/initialise_test.py b/venv/lib/python3.10/site-packages/colorama/tests/initialise_test.py new file mode 100644 index 000000000..89f9b0751 --- /dev/null +++ b/venv/lib/python3.10/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.10/site-packages/colorama/tests/isatty_test.py b/venv/lib/python3.10/site-packages/colorama/tests/isatty_test.py new file mode 100644 index 000000000..0f84e4bef --- /dev/null +++ b/venv/lib/python3.10/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.10/site-packages/colorama/tests/utils.py b/venv/lib/python3.10/site-packages/colorama/tests/utils.py new file mode 100644 index 000000000..472fafb44 --- /dev/null +++ b/venv/lib/python3.10/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.10/site-packages/colorama/tests/winterm_test.py b/venv/lib/python3.10/site-packages/colorama/tests/winterm_test.py new file mode 100644 index 000000000..d0955f9e6 --- /dev/null +++ b/venv/lib/python3.10/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.10/site-packages/colorama/win32.py b/venv/lib/python3.10/site-packages/colorama/win32.py new file mode 100644 index 000000000..841b0e270 --- /dev/null +++ b/venv/lib/python3.10/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.10/site-packages/colorama/winterm.py b/venv/lib/python3.10/site-packages/colorama/winterm.py new file mode 100644 index 000000000..aad867e8c --- /dev/null +++ b/venv/lib/python3.10/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.10/site-packages/dateutil/__init__.py b/venv/lib/python3.10/site-packages/dateutil/__init__.py new file mode 100644 index 000000000..0defb82e2 --- /dev/null +++ b/venv/lib/python3.10/site-packages/dateutil/__init__.py @@ -0,0 +1,8 @@ +# -*- coding: utf-8 -*- +try: + from ._version import version as __version__ +except ImportError: + __version__ = 'unknown' + +__all__ = ['easter', 'parser', 'relativedelta', 'rrule', 'tz', + 'utils', 'zoneinfo'] diff --git a/venv/lib/python3.10/site-packages/dateutil/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/dateutil/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 000000000..5e1630204 Binary files /dev/null and b/venv/lib/python3.10/site-packages/dateutil/__pycache__/__init__.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dateutil/__pycache__/_common.cpython-310.pyc b/venv/lib/python3.10/site-packages/dateutil/__pycache__/_common.cpython-310.pyc new file mode 100644 index 000000000..e15ed67bd Binary files /dev/null and b/venv/lib/python3.10/site-packages/dateutil/__pycache__/_common.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dateutil/__pycache__/_version.cpython-310.pyc b/venv/lib/python3.10/site-packages/dateutil/__pycache__/_version.cpython-310.pyc new file mode 100644 index 000000000..1bd1f49c9 Binary files /dev/null and b/venv/lib/python3.10/site-packages/dateutil/__pycache__/_version.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dateutil/__pycache__/easter.cpython-310.pyc b/venv/lib/python3.10/site-packages/dateutil/__pycache__/easter.cpython-310.pyc new file mode 100644 index 000000000..141283ea1 Binary files /dev/null and b/venv/lib/python3.10/site-packages/dateutil/__pycache__/easter.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dateutil/__pycache__/relativedelta.cpython-310.pyc b/venv/lib/python3.10/site-packages/dateutil/__pycache__/relativedelta.cpython-310.pyc new file mode 100644 index 000000000..504070d77 Binary files /dev/null and b/venv/lib/python3.10/site-packages/dateutil/__pycache__/relativedelta.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dateutil/__pycache__/rrule.cpython-310.pyc b/venv/lib/python3.10/site-packages/dateutil/__pycache__/rrule.cpython-310.pyc new file mode 100644 index 000000000..b53230e25 Binary files /dev/null and b/venv/lib/python3.10/site-packages/dateutil/__pycache__/rrule.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dateutil/__pycache__/tzwin.cpython-310.pyc b/venv/lib/python3.10/site-packages/dateutil/__pycache__/tzwin.cpython-310.pyc new file mode 100644 index 000000000..ebebd520e Binary files /dev/null and b/venv/lib/python3.10/site-packages/dateutil/__pycache__/tzwin.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dateutil/__pycache__/utils.cpython-310.pyc b/venv/lib/python3.10/site-packages/dateutil/__pycache__/utils.cpython-310.pyc new file mode 100644 index 000000000..5a06f1652 Binary files /dev/null and b/venv/lib/python3.10/site-packages/dateutil/__pycache__/utils.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dateutil/_common.py b/venv/lib/python3.10/site-packages/dateutil/_common.py new file mode 100644 index 000000000..4eb2659bd --- /dev/null +++ b/venv/lib/python3.10/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.10/site-packages/dateutil/_version.py b/venv/lib/python3.10/site-packages/dateutil/_version.py new file mode 100644 index 000000000..b723056a7 --- /dev/null +++ b/venv/lib/python3.10/site-packages/dateutil/_version.py @@ -0,0 +1,5 @@ +# coding: utf-8 +# file generated by setuptools_scm +# don't change, don't track in version control +version = '2.8.2' +version_tuple = (2, 8, 2) diff --git a/venv/lib/python3.10/site-packages/dateutil/easter.py b/venv/lib/python3.10/site-packages/dateutil/easter.py new file mode 100644 index 000000000..f74d1f744 --- /dev/null +++ b/venv/lib/python3.10/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.10/site-packages/dateutil/parser/__init__.py b/venv/lib/python3.10/site-packages/dateutil/parser/__init__.py new file mode 100644 index 000000000..d174b0e4d --- /dev/null +++ b/venv/lib/python3.10/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.10/site-packages/dateutil/parser/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/dateutil/parser/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 000000000..045b99c35 Binary files /dev/null and b/venv/lib/python3.10/site-packages/dateutil/parser/__pycache__/__init__.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dateutil/parser/__pycache__/_parser.cpython-310.pyc b/venv/lib/python3.10/site-packages/dateutil/parser/__pycache__/_parser.cpython-310.pyc new file mode 100644 index 000000000..22f4e0bf8 Binary files /dev/null and b/venv/lib/python3.10/site-packages/dateutil/parser/__pycache__/_parser.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dateutil/parser/__pycache__/isoparser.cpython-310.pyc b/venv/lib/python3.10/site-packages/dateutil/parser/__pycache__/isoparser.cpython-310.pyc new file mode 100644 index 000000000..695802575 Binary files /dev/null and b/venv/lib/python3.10/site-packages/dateutil/parser/__pycache__/isoparser.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dateutil/parser/_parser.py b/venv/lib/python3.10/site-packages/dateutil/parser/_parser.py new file mode 100644 index 000000000..37d1663b2 --- /dev/null +++ b/venv/lib/python3.10/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.10/site-packages/dateutil/parser/isoparser.py b/venv/lib/python3.10/site-packages/dateutil/parser/isoparser.py new file mode 100644 index 000000000..5d7bee380 --- /dev/null +++ b/venv/lib/python3.10/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`` or ``YYYYMM`` + - ``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.10/site-packages/dateutil/relativedelta.py b/venv/lib/python3.10/site-packages/dateutil/relativedelta.py new file mode 100644 index 000000000..a9e85f7e6 --- /dev/null +++ b/venv/lib/python3.10/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.10/site-packages/dateutil/rrule.py b/venv/lib/python3.10/site-packages/dateutil/rrule.py new file mode 100644 index 000000000..b3203393c --- /dev/null +++ b/venv/lib/python3.10/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 + trough 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.10/site-packages/dateutil/tz/__init__.py b/venv/lib/python3.10/site-packages/dateutil/tz/__init__.py new file mode 100644 index 000000000..af1352c47 --- /dev/null +++ b/venv/lib/python3.10/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.10/site-packages/dateutil/tz/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/dateutil/tz/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 000000000..271f5ee0f Binary files /dev/null and b/venv/lib/python3.10/site-packages/dateutil/tz/__pycache__/__init__.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dateutil/tz/__pycache__/_common.cpython-310.pyc b/venv/lib/python3.10/site-packages/dateutil/tz/__pycache__/_common.cpython-310.pyc new file mode 100644 index 000000000..30280b010 Binary files /dev/null and b/venv/lib/python3.10/site-packages/dateutil/tz/__pycache__/_common.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dateutil/tz/__pycache__/_factories.cpython-310.pyc b/venv/lib/python3.10/site-packages/dateutil/tz/__pycache__/_factories.cpython-310.pyc new file mode 100644 index 000000000..ff41fd48b Binary files /dev/null and b/venv/lib/python3.10/site-packages/dateutil/tz/__pycache__/_factories.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dateutil/tz/__pycache__/tz.cpython-310.pyc b/venv/lib/python3.10/site-packages/dateutil/tz/__pycache__/tz.cpython-310.pyc new file mode 100644 index 000000000..baf5634de Binary files /dev/null and b/venv/lib/python3.10/site-packages/dateutil/tz/__pycache__/tz.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dateutil/tz/__pycache__/win.cpython-310.pyc b/venv/lib/python3.10/site-packages/dateutil/tz/__pycache__/win.cpython-310.pyc new file mode 100644 index 000000000..3747049f1 Binary files /dev/null and b/venv/lib/python3.10/site-packages/dateutil/tz/__pycache__/win.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dateutil/tz/_common.py b/venv/lib/python3.10/site-packages/dateutil/tz/_common.py new file mode 100644 index 000000000..e6ac11831 --- /dev/null +++ b/venv/lib/python3.10/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.10/site-packages/dateutil/tz/_factories.py b/venv/lib/python3.10/site-packages/dateutil/tz/_factories.py new file mode 100644 index 000000000..f8a65891a --- /dev/null +++ b/venv/lib/python3.10/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.10/site-packages/dateutil/tz/tz.py b/venv/lib/python3.10/site-packages/dateutil/tz/tz.py new file mode 100644 index 000000000..c67f56d46 --- /dev/null +++ b/venv/lib/python3.10/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.utcfromtimestamp(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.10/site-packages/dateutil/tz/win.py b/venv/lib/python3.10/site-packages/dateutil/tz/win.py new file mode 100644 index 000000000..cde07ba79 --- /dev/null +++ b/venv/lib/python3.10/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.10/site-packages/dateutil/tzwin.py b/venv/lib/python3.10/site-packages/dateutil/tzwin.py new file mode 100644 index 000000000..cebc673e4 --- /dev/null +++ b/venv/lib/python3.10/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.10/site-packages/dateutil/utils.py b/venv/lib/python3.10/site-packages/dateutil/utils.py new file mode 100644 index 000000000..dd2d245a0 --- /dev/null +++ b/venv/lib/python3.10/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.10/site-packages/dateutil/zoneinfo/__init__.py b/venv/lib/python3.10/site-packages/dateutil/zoneinfo/__init__.py new file mode 100644 index 000000000..34f11ad66 --- /dev/null +++ b/venv/lib/python3.10/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.10/site-packages/dateutil/zoneinfo/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/dateutil/zoneinfo/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 000000000..951eac4a2 Binary files /dev/null and b/venv/lib/python3.10/site-packages/dateutil/zoneinfo/__pycache__/__init__.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dateutil/zoneinfo/__pycache__/rebuild.cpython-310.pyc b/venv/lib/python3.10/site-packages/dateutil/zoneinfo/__pycache__/rebuild.cpython-310.pyc new file mode 100644 index 000000000..b2dcad6c3 Binary files /dev/null and b/venv/lib/python3.10/site-packages/dateutil/zoneinfo/__pycache__/rebuild.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz b/venv/lib/python3.10/site-packages/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz new file mode 100644 index 000000000..524c48e12 Binary files /dev/null and b/venv/lib/python3.10/site-packages/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz differ diff --git a/venv/lib/python3.10/site-packages/dateutil/zoneinfo/rebuild.py b/venv/lib/python3.10/site-packages/dateutil/zoneinfo/rebuild.py new file mode 100644 index 000000000..684c6586f --- /dev/null +++ b/venv/lib/python3.10/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.10/site-packages/decorator-5.1.1.dist-info/INSTALLER b/venv/lib/python3.10/site-packages/decorator-5.1.1.dist-info/INSTALLER new file mode 100644 index 000000000..a1b589e38 --- /dev/null +++ b/venv/lib/python3.10/site-packages/decorator-5.1.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.10/site-packages/decorator-5.1.1.dist-info/LICENSE.txt b/venv/lib/python3.10/site-packages/decorator-5.1.1.dist-info/LICENSE.txt new file mode 100644 index 000000000..b0ade0487 --- /dev/null +++ b/venv/lib/python3.10/site-packages/decorator-5.1.1.dist-info/LICENSE.txt @@ -0,0 +1,26 @@ +Copyright (c) 2005-2018, Michele Simionato +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 bytecode 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. + +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 +HOLDERS 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.10/site-packages/decorator-5.1.1.dist-info/METADATA b/venv/lib/python3.10/site-packages/decorator-5.1.1.dist-info/METADATA new file mode 100644 index 000000000..df407f805 --- /dev/null +++ b/venv/lib/python3.10/site-packages/decorator-5.1.1.dist-info/METADATA @@ -0,0 +1,127 @@ +Metadata-Version: 2.1 +Name: decorator +Version: 5.1.1 +Summary: Decorators for Humans +Home-page: https://github.com/micheles/decorator +Author: Michele Simionato +Author-email: michele.simionato@gmail.com +License: new BSD License +Keywords: decorators generic utility +Platform: All +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Natural Language :: English +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3.5 +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 :: Implementation :: CPython +Classifier: Topic :: Software Development :: Libraries +Classifier: Topic :: Utilities +Requires-Python: >=3.5 + +Decorators for Humans +===================== + +The goal of the decorator module is to make it easy to define +signature-preserving function decorators and decorator factories. +It also includes an implementation of multiple dispatch and other niceties +(please check the docs). It is released under a two-clauses +BSD license, i.e. basically you can do whatever you want with it but I am not +responsible. + +Installation +------------- + +If you are lazy, just perform + + ``$ pip install decorator`` + +which will install just the module on your system. + +If you prefer to install the full distribution from source, including +the documentation, clone the `GitHub repo`_ or download the tarball_, unpack it and run + + ``$ pip install .`` + +in the main directory, possibly as superuser. + +.. _tarball: https://pypi.org/project/decorator/#files +.. _GitHub repo: https://github.com/micheles/decorator + +Testing +-------- + +If you have the source code installation you can run the tests with + + `$ python src/tests/test.py -v` + +or (if you have setuptools installed) + + `$ python setup.py test` + +Notice that you may run into trouble if in your system there +is an older version of the decorator module; in such a case remove the +old version. It is safe even to copy the module `decorator.py` over +an existing one, since we kept backward-compatibility for a long time. + +Repository +--------------- + +The project is hosted on GitHub. You can look at the source here: + + https://github.com/micheles/decorator + +Documentation +--------------- + +The documentation has been moved to https://github.com/micheles/decorator/blob/master/docs/documentation.md + +From there you can get a PDF version by simply using the print +functionality of your browser. + +Here is the documentation for previous versions of the module: + +https://github.com/micheles/decorator/blob/4.3.2/docs/tests.documentation.rst +https://github.com/micheles/decorator/blob/4.2.1/docs/tests.documentation.rst +https://github.com/micheles/decorator/blob/4.1.2/docs/tests.documentation.rst +https://github.com/micheles/decorator/blob/4.0.0/documentation.rst +https://github.com/micheles/decorator/blob/3.4.2/documentation.rst + +For the impatient +----------------- + +Here is an example of how to define a family of decorators tracing slow +operations: + +.. code-block:: python + + from decorator import decorator + + @decorator + def warn_slow(func, timelimit=60, *args, **kw): + t0 = time.time() + result = func(*args, **kw) + dt = time.time() - t0 + if dt > timelimit: + logging.warn('%s took %d seconds', func.__name__, dt) + else: + logging.info('%s took %d seconds', func.__name__, dt) + return result + + @warn_slow # warn if it takes more than 1 minute + def preprocess_input_files(inputdir, tempdir): + ... + + @warn_slow(timelimit=600) # warn if it takes more than 10 minutes + def run_calculation(tempdir, outdir): + ... + +Enjoy! + + diff --git a/venv/lib/python3.10/site-packages/decorator-5.1.1.dist-info/RECORD b/venv/lib/python3.10/site-packages/decorator-5.1.1.dist-info/RECORD new file mode 100644 index 000000000..bea9cbbb9 --- /dev/null +++ b/venv/lib/python3.10/site-packages/decorator-5.1.1.dist-info/RECORD @@ -0,0 +1,9 @@ +__pycache__/decorator.cpython-310.pyc,, +decorator-5.1.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +decorator-5.1.1.dist-info/LICENSE.txt,sha256=_RFmDKvwUyCCxFcGhi-vwpSQfsf44heBgkCkmZgGeC4,1309 +decorator-5.1.1.dist-info/METADATA,sha256=XAr2zbYpRxCkcPbsmg1oaiS5ea7mhTq-j-wb0XjuVho,3955 +decorator-5.1.1.dist-info/RECORD,, +decorator-5.1.1.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92 +decorator-5.1.1.dist-info/pbr.json,sha256=AL84oUUWQHwkd8OCPhLRo2NJjU5MDdmXMqRHv-posqs,47 +decorator-5.1.1.dist-info/top_level.txt,sha256=Kn6eQjo83ctWxXVyBMOYt0_YpjRjBznKYVuNyuC_DSI,10 +decorator.py,sha256=el5cAEgoTEpRQN65tOxGhElue-CccMv0xol-J2MwOc0,16752 diff --git a/venv/lib/python3.10/site-packages/decorator-5.1.1.dist-info/WHEEL b/venv/lib/python3.10/site-packages/decorator-5.1.1.dist-info/WHEEL new file mode 100644 index 000000000..5bad85fdc --- /dev/null +++ b/venv/lib/python3.10/site-packages/decorator-5.1.1.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.37.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/venv/lib/python3.10/site-packages/decorator-5.1.1.dist-info/pbr.json b/venv/lib/python3.10/site-packages/decorator-5.1.1.dist-info/pbr.json new file mode 100644 index 000000000..cd0459978 --- /dev/null +++ b/venv/lib/python3.10/site-packages/decorator-5.1.1.dist-info/pbr.json @@ -0,0 +1 @@ +{"is_release": false, "git_version": "8608a46"} \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/decorator-5.1.1.dist-info/top_level.txt b/venv/lib/python3.10/site-packages/decorator-5.1.1.dist-info/top_level.txt new file mode 100644 index 000000000..3fe18a4d1 --- /dev/null +++ b/venv/lib/python3.10/site-packages/decorator-5.1.1.dist-info/top_level.txt @@ -0,0 +1 @@ +decorator diff --git a/venv/lib/python3.10/site-packages/decorator.py b/venv/lib/python3.10/site-packages/decorator.py new file mode 100644 index 000000000..2479b6f7b --- /dev/null +++ b/venv/lib/python3.10/site-packages/decorator.py @@ -0,0 +1,451 @@ +# ######################### LICENSE ############################ # + +# Copyright (c) 2005-2021, Michele Simionato +# 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 bytecode 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. + +# 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 +# HOLDERS 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. + +""" +Decorator module, see +https://github.com/micheles/decorator/blob/master/docs/documentation.md +for the documentation. +""" +import re +import sys +import inspect +import operator +import itertools +from contextlib import _GeneratorContextManager +from inspect import getfullargspec, iscoroutinefunction, isgeneratorfunction + +__version__ = '5.1.1' + +DEF = re.compile(r'\s*def\s*([_\w][_\w\d]*)\s*\(') +POS = inspect.Parameter.POSITIONAL_OR_KEYWORD +EMPTY = inspect.Parameter.empty + + +# this is not used anymore in the core, but kept for backward compatibility +class FunctionMaker(object): + """ + An object with the ability to create functions with a given signature. + It has attributes name, doc, module, signature, defaults, dict and + methods update and make. + """ + + # Atomic get-and-increment provided by the GIL + _compile_count = itertools.count() + + # make pylint happy + args = varargs = varkw = defaults = kwonlyargs = kwonlydefaults = () + + def __init__(self, func=None, name=None, signature=None, + defaults=None, doc=None, module=None, funcdict=None): + self.shortsignature = signature + if func: + # func can be a class or a callable, but not an instance method + self.name = func.__name__ + if self.name == '': # small hack for lambda functions + self.name = '_lambda_' + self.doc = func.__doc__ + self.module = func.__module__ + if inspect.isroutine(func): + argspec = getfullargspec(func) + self.annotations = getattr(func, '__annotations__', {}) + for a in ('args', 'varargs', 'varkw', 'defaults', 'kwonlyargs', + 'kwonlydefaults'): + setattr(self, a, getattr(argspec, a)) + for i, arg in enumerate(self.args): + setattr(self, 'arg%d' % i, arg) + allargs = list(self.args) + allshortargs = list(self.args) + if self.varargs: + allargs.append('*' + self.varargs) + allshortargs.append('*' + self.varargs) + elif self.kwonlyargs: + allargs.append('*') # single star syntax + for a in self.kwonlyargs: + allargs.append('%s=None' % a) + allshortargs.append('%s=%s' % (a, a)) + if self.varkw: + allargs.append('**' + self.varkw) + allshortargs.append('**' + self.varkw) + self.signature = ', '.join(allargs) + self.shortsignature = ', '.join(allshortargs) + self.dict = func.__dict__.copy() + # func=None happens when decorating a caller + if name: + self.name = name + if signature is not None: + self.signature = signature + if defaults: + self.defaults = defaults + if doc: + self.doc = doc + if module: + self.module = module + if funcdict: + self.dict = funcdict + # check existence required attributes + assert hasattr(self, 'name') + if not hasattr(self, 'signature'): + raise TypeError('You are decorating a non function: %s' % func) + + def update(self, func, **kw): + """ + Update the signature of func with the data in self + """ + func.__name__ = self.name + func.__doc__ = getattr(self, 'doc', None) + func.__dict__ = getattr(self, 'dict', {}) + func.__defaults__ = self.defaults + func.__kwdefaults__ = self.kwonlydefaults or None + func.__annotations__ = getattr(self, 'annotations', None) + try: + frame = sys._getframe(3) + except AttributeError: # for IronPython and similar implementations + callermodule = '?' + else: + callermodule = frame.f_globals.get('__name__', '?') + func.__module__ = getattr(self, 'module', callermodule) + func.__dict__.update(kw) + + def make(self, src_templ, evaldict=None, addsource=False, **attrs): + """ + Make a new function from a given template and update the signature + """ + src = src_templ % vars(self) # expand name and signature + evaldict = evaldict or {} + mo = DEF.search(src) + if mo is None: + raise SyntaxError('not a valid function template\n%s' % src) + name = mo.group(1) # extract the function name + names = set([name] + [arg.strip(' *') for arg in + self.shortsignature.split(',')]) + for n in names: + if n in ('_func_', '_call_'): + raise NameError('%s is overridden in\n%s' % (n, src)) + + if not src.endswith('\n'): # add a newline for old Pythons + src += '\n' + + # Ensure each generated function has a unique filename for profilers + # (such as cProfile) that depend on the tuple of (, + # , ) being unique. + filename = '' % next(self._compile_count) + try: + code = compile(src, filename, 'single') + exec(code, evaldict) + except Exception: + print('Error in generated code:', file=sys.stderr) + print(src, file=sys.stderr) + raise + func = evaldict[name] + if addsource: + attrs['__source__'] = src + self.update(func, **attrs) + return func + + @classmethod + def create(cls, obj, body, evaldict, defaults=None, + doc=None, module=None, addsource=True, **attrs): + """ + Create a function from the strings name, signature and body. + evaldict is the evaluation dictionary. If addsource is true an + attribute __source__ is added to the result. The attributes attrs + are added, if any. + """ + if isinstance(obj, str): # "name(signature)" + name, rest = obj.strip().split('(', 1) + signature = rest[:-1] # strip a right parens + func = None + else: # a function + name = None + signature = None + func = obj + self = cls(func, name, signature, defaults, doc, module) + ibody = '\n'.join(' ' + line for line in body.splitlines()) + caller = evaldict.get('_call_') # when called from `decorate` + if caller and iscoroutinefunction(caller): + body = ('async def %(name)s(%(signature)s):\n' + ibody).replace( + 'return', 'return await') + else: + body = 'def %(name)s(%(signature)s):\n' + ibody + return self.make(body, evaldict, addsource, **attrs) + + +def fix(args, kwargs, sig): + """ + Fix args and kwargs to be consistent with the signature + """ + ba = sig.bind(*args, **kwargs) + ba.apply_defaults() # needed for test_dan_schult + return ba.args, ba.kwargs + + +def decorate(func, caller, extras=(), kwsyntax=False): + """ + Decorates a function/generator/coroutine using a caller. + If kwsyntax is True calling the decorated functions with keyword + syntax will pass the named arguments inside the ``kw`` dictionary, + even if such argument are positional, similarly to what functools.wraps + does. By default kwsyntax is False and the the arguments are untouched. + """ + sig = inspect.signature(func) + if iscoroutinefunction(caller): + async def fun(*args, **kw): + if not kwsyntax: + args, kw = fix(args, kw, sig) + return await caller(func, *(extras + args), **kw) + elif isgeneratorfunction(caller): + def fun(*args, **kw): + if not kwsyntax: + args, kw = fix(args, kw, sig) + for res in caller(func, *(extras + args), **kw): + yield res + else: + def fun(*args, **kw): + if not kwsyntax: + args, kw = fix(args, kw, sig) + return caller(func, *(extras + args), **kw) + fun.__name__ = func.__name__ + fun.__doc__ = func.__doc__ + fun.__wrapped__ = func + fun.__signature__ = sig + fun.__qualname__ = func.__qualname__ + # builtin functions like defaultdict.__setitem__ lack many attributes + try: + fun.__defaults__ = func.__defaults__ + except AttributeError: + pass + try: + fun.__kwdefaults__ = func.__kwdefaults__ + except AttributeError: + pass + try: + fun.__annotations__ = func.__annotations__ + except AttributeError: + pass + try: + fun.__module__ = func.__module__ + except AttributeError: + pass + try: + fun.__dict__.update(func.__dict__) + except AttributeError: + pass + return fun + + +def decoratorx(caller): + """ + A version of "decorator" implemented via "exec" and not via the + Signature object. Use this if you are want to preserve the `.__code__` + object properties (https://github.com/micheles/decorator/issues/129). + """ + def dec(func): + return FunctionMaker.create( + func, + "return _call_(_func_, %(shortsignature)s)", + dict(_call_=caller, _func_=func), + __wrapped__=func, __qualname__=func.__qualname__) + return dec + + +def decorator(caller, _func=None, kwsyntax=False): + """ + decorator(caller) converts a caller function into a decorator + """ + if _func is not None: # return a decorated function + # this is obsolete behavior; you should use decorate instead + return decorate(_func, caller, (), kwsyntax) + # else return a decorator function + sig = inspect.signature(caller) + dec_params = [p for p in sig.parameters.values() if p.kind is POS] + + def dec(func=None, *args, **kw): + na = len(args) + 1 + extras = args + tuple(kw.get(p.name, p.default) + for p in dec_params[na:] + if p.default is not EMPTY) + if func is None: + return lambda func: decorate(func, caller, extras, kwsyntax) + else: + return decorate(func, caller, extras, kwsyntax) + dec.__signature__ = sig.replace(parameters=dec_params) + dec.__name__ = caller.__name__ + dec.__doc__ = caller.__doc__ + dec.__wrapped__ = caller + dec.__qualname__ = caller.__qualname__ + dec.__kwdefaults__ = getattr(caller, '__kwdefaults__', None) + dec.__dict__.update(caller.__dict__) + return dec + + +# ####################### contextmanager ####################### # + + +class ContextManager(_GeneratorContextManager): + def __init__(self, g, *a, **k): + _GeneratorContextManager.__init__(self, g, a, k) + + def __call__(self, func): + def caller(f, *a, **k): + with self.__class__(self.func, *self.args, **self.kwds): + return f(*a, **k) + return decorate(func, caller) + + +_contextmanager = decorator(ContextManager) + + +def contextmanager(func): + # Enable Pylint config: contextmanager-decorators=decorator.contextmanager + return _contextmanager(func) + + +# ############################ dispatch_on ############################ # + +def append(a, vancestors): + """ + Append ``a`` to the list of the virtual ancestors, unless it is already + included. + """ + add = True + for j, va in enumerate(vancestors): + if issubclass(va, a): + add = False + break + if issubclass(a, va): + vancestors[j] = a + add = False + if add: + vancestors.append(a) + + +# inspired from simplegeneric by P.J. Eby and functools.singledispatch +def dispatch_on(*dispatch_args): + """ + Factory of decorators turning a function into a generic function + dispatching on the given arguments. + """ + assert dispatch_args, 'No dispatch args passed' + dispatch_str = '(%s,)' % ', '.join(dispatch_args) + + def check(arguments, wrong=operator.ne, msg=''): + """Make sure one passes the expected number of arguments""" + if wrong(len(arguments), len(dispatch_args)): + raise TypeError('Expected %d arguments, got %d%s' % + (len(dispatch_args), len(arguments), msg)) + + def gen_func_dec(func): + """Decorator turning a function into a generic function""" + + # first check the dispatch arguments + argset = set(getfullargspec(func).args) + if not set(dispatch_args) <= argset: + raise NameError('Unknown dispatch arguments %s' % dispatch_str) + + typemap = {} + + def vancestors(*types): + """ + Get a list of sets of virtual ancestors for the given types + """ + check(types) + ras = [[] for _ in range(len(dispatch_args))] + for types_ in typemap: + for t, type_, ra in zip(types, types_, ras): + if issubclass(t, type_) and type_ not in t.mro(): + append(type_, ra) + return [set(ra) for ra in ras] + + def ancestors(*types): + """ + Get a list of virtual MROs, one for each type + """ + check(types) + lists = [] + for t, vas in zip(types, vancestors(*types)): + n_vas = len(vas) + if n_vas > 1: + raise RuntimeError( + 'Ambiguous dispatch for %s: %s' % (t, vas)) + elif n_vas == 1: + va, = vas + mro = type('t', (t, va), {}).mro()[1:] + else: + mro = t.mro() + lists.append(mro[:-1]) # discard t and object + return lists + + def register(*types): + """ + Decorator to register an implementation for the given types + """ + check(types) + + def dec(f): + check(getfullargspec(f).args, operator.lt, ' in ' + f.__name__) + typemap[types] = f + return f + return dec + + def dispatch_info(*types): + """ + An utility to introspect the dispatch algorithm + """ + check(types) + lst = [] + for anc in itertools.product(*ancestors(*types)): + lst.append(tuple(a.__name__ for a in anc)) + return lst + + def _dispatch(dispatch_args, *args, **kw): + types = tuple(type(arg) for arg in dispatch_args) + try: # fast path + f = typemap[types] + except KeyError: + pass + else: + return f(*args, **kw) + combinations = itertools.product(*ancestors(*types)) + next(combinations) # the first one has been already tried + for types_ in combinations: + f = typemap.get(types_) + if f is not None: + return f(*args, **kw) + + # else call the default implementation + return func(*args, **kw) + + return FunctionMaker.create( + func, 'return _f_(%s, %%(shortsignature)s)' % dispatch_str, + dict(_f_=_dispatch), register=register, default=func, + typemap=typemap, vancestors=vancestors, ancestors=ancestors, + dispatch_info=dispatch_info, __wrapped__=func) + + gen_func_dec.__name__ = 'dispatch_on' + dispatch_str + return gen_func_dec diff --git a/venv/lib/python3.10/site-packages/distutils-precedence.pth b/venv/lib/python3.10/site-packages/distutils-precedence.pth new file mode 100644 index 000000000..7f009fe9b --- /dev/null +++ b/venv/lib/python3.10/site-packages/distutils-precedence.pth @@ -0,0 +1 @@ +import os; var = 'SETUPTOOLS_USE_DISTUTILS'; enabled = os.environ.get(var, 'local') == 'local'; enabled and __import__('_distutils_hack').add_shim(); diff --git a/venv/lib/python3.10/site-packages/ghp_import-2.1.0.dist-info/INSTALLER b/venv/lib/python3.10/site-packages/ghp_import-2.1.0.dist-info/INSTALLER new file mode 100644 index 000000000..a1b589e38 --- /dev/null +++ b/venv/lib/python3.10/site-packages/ghp_import-2.1.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.10/site-packages/ghp_import-2.1.0.dist-info/LICENSE b/venv/lib/python3.10/site-packages/ghp_import-2.1.0.dist-info/LICENSE new file mode 100644 index 000000000..756d048c4 --- /dev/null +++ b/venv/lib/python3.10/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.10/site-packages/ghp_import-2.1.0.dist-info/METADATA b/venv/lib/python3.10/site-packages/ghp_import-2.1.0.dist-info/METADATA new file mode 100644 index 000000000..70c12664d --- /dev/null +++ b/venv/lib/python3.10/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.10/site-packages/ghp_import-2.1.0.dist-info/RECORD b/venv/lib/python3.10/site-packages/ghp_import-2.1.0.dist-info/RECORD new file mode 100644 index 000000000..b6a7b996f --- /dev/null +++ b/venv/lib/python3.10/site-packages/ghp_import-2.1.0.dist-info/RECORD @@ -0,0 +1,10 @@ +../../../bin/ghp-import,sha256=p4uYmDqnHkhGhajoKAiQ0af_M9kJ73NNw_ODplm-2Jo,284 +__pycache__/ghp_import.cpython-310.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.10/site-packages/ghp_import-2.1.0.dist-info/WHEEL b/venv/lib/python3.10/site-packages/ghp_import-2.1.0.dist-info/WHEEL new file mode 100644 index 000000000..becc9a66e --- /dev/null +++ b/venv/lib/python3.10/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.10/site-packages/ghp_import-2.1.0.dist-info/entry_points.txt b/venv/lib/python3.10/site-packages/ghp_import-2.1.0.dist-info/entry_points.txt new file mode 100644 index 000000000..6f2959a8b --- /dev/null +++ b/venv/lib/python3.10/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.10/site-packages/ghp_import-2.1.0.dist-info/top_level.txt b/venv/lib/python3.10/site-packages/ghp_import-2.1.0.dist-info/top_level.txt new file mode 100644 index 000000000..a780ceada --- /dev/null +++ b/venv/lib/python3.10/site-packages/ghp_import-2.1.0.dist-info/top_level.txt @@ -0,0 +1 @@ +ghp_import diff --git a/venv/lib/python3.10/site-packages/ghp_import.py b/venv/lib/python3.10/site-packages/ghp_import.py new file mode 100644 index 000000000..328422bdf --- /dev/null +++ b/venv/lib/python3.10/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.10/site-packages/git/__init__.py b/venv/lib/python3.10/site-packages/git/__init__.py new file mode 100644 index 000000000..ff025b801 --- /dev/null +++ b/venv/lib/python3.10/site-packages/git/__init__.py @@ -0,0 +1,92 @@ +# __init__.py +# Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors +# +# This module is part of GitPython and is released under +# the BSD License: http://www.opensource.org/licenses/bsd-license.php +# flake8: noqa +# @PydevCodeAnalysisIgnore +from git.exc import * # @NoMove @IgnorePep8 +import inspect +import os +import sys +import os.path as osp + +from typing import Optional +from git.types import PathLike + +__version__ = '3.1.31' + + +# { Initialization +def _init_externals() -> None: + """Initialize external projects by putting them into the path""" + if __version__ == '3.1.31' and "PYOXIDIZER" not in os.environ: + sys.path.insert(1, osp.join(osp.dirname(__file__), "ext", "gitdb")) + + try: + import gitdb + except ImportError as e: + raise ImportError("'gitdb' could not be found in your PYTHONPATH") from e + # END verify import + + +# } END initialization + + +################# +_init_externals() +################# + +# { Imports + +try: + from git.config import GitConfigParser # @NoMove @IgnorePep8 + from git.objects import * # @NoMove @IgnorePep8 + from git.refs import * # @NoMove @IgnorePep8 + from git.diff import * # @NoMove @IgnorePep8 + from git.db import * # @NoMove @IgnorePep8 + from git.cmd import Git # @NoMove @IgnorePep8 + from git.repo import Repo # @NoMove @IgnorePep8 + from git.remote import * # @NoMove @IgnorePep8 + from git.index import * # @NoMove @IgnorePep8 + from git.util import ( # @NoMove @IgnorePep8 + LockFile, + BlockingLockFile, + Stats, + Actor, + rmtree, + ) +except GitError as exc: + raise ImportError("%s: %s" % (exc.__class__.__name__, exc)) from exc + +# } END imports + +__all__ = [name for name, obj in locals().items() if not (name.startswith("_") or inspect.ismodule(obj))] + + +# { Initialize git executable path +GIT_OK = None + + +def refresh(path: Optional[PathLike] = None) -> None: + """Convenience method for setting the git executable path.""" + global GIT_OK + GIT_OK = False + + if not Git.refresh(path=path): + return + if not FetchInfo.refresh(): + return + + GIT_OK = True + + +# } END initialize git executable path + + +################# +try: + refresh() +except Exception as exc: + raise ImportError("Failed to initialize: {0}".format(exc)) from exc +################# diff --git a/venv/lib/python3.10/site-packages/git/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/git/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 000000000..efd2c59eb Binary files /dev/null and b/venv/lib/python3.10/site-packages/git/__pycache__/__init__.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/git/__pycache__/cmd.cpython-310.pyc b/venv/lib/python3.10/site-packages/git/__pycache__/cmd.cpython-310.pyc new file mode 100644 index 000000000..ac6ec1266 Binary files /dev/null and b/venv/lib/python3.10/site-packages/git/__pycache__/cmd.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/git/__pycache__/compat.cpython-310.pyc b/venv/lib/python3.10/site-packages/git/__pycache__/compat.cpython-310.pyc new file mode 100644 index 000000000..f3286029d Binary files /dev/null and b/venv/lib/python3.10/site-packages/git/__pycache__/compat.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/git/__pycache__/config.cpython-310.pyc b/venv/lib/python3.10/site-packages/git/__pycache__/config.cpython-310.pyc new file mode 100644 index 000000000..d49118d55 Binary files /dev/null and b/venv/lib/python3.10/site-packages/git/__pycache__/config.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/git/__pycache__/db.cpython-310.pyc b/venv/lib/python3.10/site-packages/git/__pycache__/db.cpython-310.pyc new file mode 100644 index 000000000..083c7a709 Binary files /dev/null and b/venv/lib/python3.10/site-packages/git/__pycache__/db.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/git/__pycache__/diff.cpython-310.pyc b/venv/lib/python3.10/site-packages/git/__pycache__/diff.cpython-310.pyc new file mode 100644 index 000000000..e27a8fab9 Binary files /dev/null and b/venv/lib/python3.10/site-packages/git/__pycache__/diff.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/git/__pycache__/exc.cpython-310.pyc b/venv/lib/python3.10/site-packages/git/__pycache__/exc.cpython-310.pyc new file mode 100644 index 000000000..01069b5c7 Binary files /dev/null and b/venv/lib/python3.10/site-packages/git/__pycache__/exc.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/git/__pycache__/remote.cpython-310.pyc b/venv/lib/python3.10/site-packages/git/__pycache__/remote.cpython-310.pyc new file mode 100644 index 000000000..6a80ff555 Binary files /dev/null and b/venv/lib/python3.10/site-packages/git/__pycache__/remote.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/git/__pycache__/types.cpython-310.pyc b/venv/lib/python3.10/site-packages/git/__pycache__/types.cpython-310.pyc new file mode 100644 index 000000000..2e50d187c Binary files /dev/null and b/venv/lib/python3.10/site-packages/git/__pycache__/types.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/git/__pycache__/util.cpython-310.pyc b/venv/lib/python3.10/site-packages/git/__pycache__/util.cpython-310.pyc new file mode 100644 index 000000000..e267c3d2c Binary files /dev/null and b/venv/lib/python3.10/site-packages/git/__pycache__/util.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/git/cmd.py b/venv/lib/python3.10/site-packages/git/cmd.py new file mode 100644 index 000000000..dfce9024d --- /dev/null +++ b/venv/lib/python3.10/site-packages/git/cmd.py @@ -0,0 +1,1417 @@ +# cmd.py +# Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors +# +# This module is part of GitPython and is released under +# the BSD License: http://www.opensource.org/licenses/bsd-license.php +from __future__ import annotations +import re +from contextlib import contextmanager +import io +import logging +import os +import signal +from subprocess import call, Popen, PIPE, DEVNULL +import subprocess +import threading +from textwrap import dedent + +from git.compat import ( + defenc, + force_bytes, + safe_decode, + is_posix, + is_win, +) +from git.exc import CommandError +from git.util import is_cygwin_git, cygpath, expand_path, remove_password_if_present + +from .exc import GitCommandError, GitCommandNotFound, UnsafeOptionError, UnsafeProtocolError +from .util import ( + LazyMixin, + stream_copy, +) + +# typing --------------------------------------------------------------------------- + +from typing import ( + Any, + AnyStr, + BinaryIO, + Callable, + Dict, + IO, + Iterator, + List, + Mapping, + Sequence, + TYPE_CHECKING, + TextIO, + Tuple, + Union, + cast, + overload, +) + +from git.types import PathLike, Literal, TBD + +if TYPE_CHECKING: + from git.repo.base import Repo + from git.diff import DiffIndex + + +# --------------------------------------------------------------------------------- + +execute_kwargs = { + "istream", + "with_extended_output", + "with_exceptions", + "as_process", + "stdout_as_string", + "output_stream", + "with_stdout", + "kill_after_timeout", + "universal_newlines", + "shell", + "env", + "max_chunk_size", + "strip_newline_in_stdout", +} + +log = logging.getLogger(__name__) +log.addHandler(logging.NullHandler()) + +__all__ = ("Git",) + + +# ============================================================================== +## @name Utilities +# ------------------------------------------------------------------------------ +# Documentation +## @{ + + +def handle_process_output( + process: "Git.AutoInterrupt" | Popen, + stdout_handler: Union[ + None, + Callable[[AnyStr], None], + Callable[[List[AnyStr]], None], + Callable[[bytes, "Repo", "DiffIndex"], None], + ], + stderr_handler: Union[None, Callable[[AnyStr], None], Callable[[List[AnyStr]], None]], + finalizer: Union[None, Callable[[Union[subprocess.Popen, "Git.AutoInterrupt"]], None]] = None, + decode_streams: bool = True, + kill_after_timeout: Union[None, float] = None, +) -> None: + """Registers for notifications to learn that process output is ready to read, and dispatches lines to + the respective line handlers. + This function returns once the finalizer returns + + :return: result of finalizer + :param process: subprocess.Popen instance + :param stdout_handler: f(stdout_line_string), or None + :param stderr_handler: f(stderr_line_string), or None + :param finalizer: f(proc) - wait for proc to finish + :param decode_streams: + Assume stdout/stderr streams are binary and decode them before pushing \ + their contents to handlers. + Set it to False if `universal_newline == True` (then streams are in text-mode) + or if decoding must happen later (i.e. for Diffs). + :param kill_after_timeout: + float or None, Default = None + To specify a timeout in seconds for the git command, after which the process + should be killed. + """ + # Use 2 "pump" threads and wait for both to finish. + def pump_stream( + cmdline: List[str], + name: str, + stream: Union[BinaryIO, TextIO], + is_decode: bool, + handler: Union[None, Callable[[Union[bytes, str]], None]], + ) -> None: + try: + for line in stream: + if handler: + if is_decode: + assert isinstance(line, bytes) + line_str = line.decode(defenc) + handler(line_str) + else: + handler(line) + + except Exception as ex: + log.error(f"Pumping {name!r} of cmd({remove_password_if_present(cmdline)}) failed due to: {ex!r}") + if "I/O operation on closed file" not in str(ex): + # Only reraise if the error was not due to the stream closing + raise CommandError([f"<{name}-pump>"] + remove_password_if_present(cmdline), ex) from ex + finally: + stream.close() + + if hasattr(process, "proc"): + process = cast("Git.AutoInterrupt", process) + cmdline: str | Tuple[str, ...] | List[str] = getattr(process.proc, "args", "") + p_stdout = process.proc.stdout if process.proc else None + p_stderr = process.proc.stderr if process.proc else None + else: + process = cast(Popen, process) + cmdline = getattr(process, "args", "") + p_stdout = process.stdout + p_stderr = process.stderr + + if not isinstance(cmdline, (tuple, list)): + cmdline = cmdline.split() + + pumps: List[Tuple[str, IO, Callable[..., None] | None]] = [] + if p_stdout: + pumps.append(("stdout", p_stdout, stdout_handler)) + if p_stderr: + pumps.append(("stderr", p_stderr, stderr_handler)) + + threads: List[threading.Thread] = [] + + for name, stream, handler in pumps: + t = threading.Thread(target=pump_stream, args=(cmdline, name, stream, decode_streams, handler)) + t.daemon = True + t.start() + threads.append(t) + + ## FIXME: Why Join?? Will block if `stdin` needs feeding... + # + for t in threads: + t.join(timeout=kill_after_timeout) + if t.is_alive(): + if isinstance(process, Git.AutoInterrupt): + process._terminate() + else: # Don't want to deal with the other case + raise RuntimeError( + "Thread join() timed out in cmd.handle_process_output()." + f" kill_after_timeout={kill_after_timeout} seconds" + ) + if stderr_handler: + error_str: Union[str, bytes] = ( + "error: process killed because it timed out." f" kill_after_timeout={kill_after_timeout} seconds" + ) + if not decode_streams and isinstance(p_stderr, BinaryIO): + # Assume stderr_handler needs binary input + error_str = cast(str, error_str) + error_str = error_str.encode() + # We ignore typing on the next line because mypy does not like + # the way we inferred that stderr takes str or bytes + stderr_handler(error_str) # type: ignore + + if finalizer: + return finalizer(process) + else: + return None + + +def dashify(string: str) -> str: + return string.replace("_", "-") + + +def slots_to_dict(self: object, exclude: Sequence[str] = ()) -> Dict[str, Any]: + return {s: getattr(self, s) for s in self.__slots__ if s not in exclude} + + +def dict_to_slots_and__excluded_are_none(self: object, d: Mapping[str, Any], excluded: Sequence[str] = ()) -> None: + for k, v in d.items(): + setattr(self, k, v) + for k in excluded: + setattr(self, k, None) + + +## -- End Utilities -- @} + + +# value of Windows process creation flag taken from MSDN +CREATE_NO_WINDOW = 0x08000000 + +## CREATE_NEW_PROCESS_GROUP is needed to allow killing it afterwards, +# see https://docs.python.org/3/library/subprocess.html#subprocess.Popen.send_signal +PROC_CREATIONFLAGS = ( + CREATE_NO_WINDOW | subprocess.CREATE_NEW_PROCESS_GROUP if is_win else 0 # type: ignore[attr-defined] +) # mypy error if not windows + + +class Git(LazyMixin): + + """ + The Git class manages communication with the Git binary. + + It provides a convenient interface to calling the Git binary, such as in:: + + g = Git( git_dir ) + g.init() # calls 'git init' program + rval = g.ls_files() # calls 'git ls-files' program + + ``Debugging`` + Set the GIT_PYTHON_TRACE environment variable print each invocation + of the command to stdout. + Set its value to 'full' to see details about the returned values. + """ + + __slots__ = ( + "_working_dir", + "cat_file_all", + "cat_file_header", + "_version_info", + "_git_options", + "_persistent_git_options", + "_environment", + ) + + _excluded_ = ("cat_file_all", "cat_file_header", "_version_info") + + re_unsafe_protocol = re.compile("(.+)::.+") + + def __getstate__(self) -> Dict[str, Any]: + return slots_to_dict(self, exclude=self._excluded_) + + def __setstate__(self, d: Dict[str, Any]) -> None: + dict_to_slots_and__excluded_are_none(self, d, excluded=self._excluded_) + + # CONFIGURATION + + git_exec_name = "git" # default that should work on linux and windows + + # Enables debugging of GitPython's git commands + GIT_PYTHON_TRACE = os.environ.get("GIT_PYTHON_TRACE", False) + + # If True, a shell will be used when executing git commands. + # This should only be desirable on Windows, see https://github.com/gitpython-developers/GitPython/pull/126 + # and check `git/test_repo.py:TestRepo.test_untracked_files()` TC for an example where it is required. + # Override this value using `Git.USE_SHELL = True` + USE_SHELL = False + + # Provide the full path to the git executable. Otherwise it assumes git is in the path + _git_exec_env_var = "GIT_PYTHON_GIT_EXECUTABLE" + _refresh_env_var = "GIT_PYTHON_REFRESH" + GIT_PYTHON_GIT_EXECUTABLE = None + # note that the git executable is actually found during the refresh step in + # the top level __init__ + + @classmethod + def refresh(cls, path: Union[None, PathLike] = None) -> bool: + """This gets called by the refresh function (see the top level + __init__). + """ + # discern which path to refresh with + if path is not None: + new_git = os.path.expanduser(path) + new_git = os.path.abspath(new_git) + else: + new_git = os.environ.get(cls._git_exec_env_var, cls.git_exec_name) + + # keep track of the old and new git executable path + old_git = cls.GIT_PYTHON_GIT_EXECUTABLE + cls.GIT_PYTHON_GIT_EXECUTABLE = new_git + + # test if the new git executable path is valid + + # - a GitCommandNotFound error is spawned by ourselves + # - a PermissionError is spawned if the git executable provided + # cannot be executed for whatever reason + + has_git = False + try: + cls().version() + has_git = True + except (GitCommandNotFound, PermissionError): + pass + + # warn or raise exception if test failed + if not has_git: + err = ( + dedent( + """\ + Bad git executable. + The git executable must be specified in one of the following ways: + - be included in your $PATH + - be set via $%s + - explicitly set via git.refresh() + """ + ) + % cls._git_exec_env_var + ) + + # revert to whatever the old_git was + cls.GIT_PYTHON_GIT_EXECUTABLE = old_git + + if old_git is None: + # on the first refresh (when GIT_PYTHON_GIT_EXECUTABLE is + # None) we only are quiet, warn, or error depending on the + # GIT_PYTHON_REFRESH value + + # determine what the user wants to happen during the initial + # refresh we expect GIT_PYTHON_REFRESH to either be unset or + # be one of the following values: + # 0|q|quiet|s|silence + # 1|w|warn|warning + # 2|r|raise|e|error + + mode = os.environ.get(cls._refresh_env_var, "raise").lower() + + quiet = ["quiet", "q", "silence", "s", "none", "n", "0"] + warn = ["warn", "w", "warning", "1"] + error = ["error", "e", "raise", "r", "2"] + + if mode in quiet: + pass + elif mode in warn or mode in error: + err = ( + dedent( + """\ + %s + All git commands will error until this is rectified. + + This initial warning can be silenced or aggravated in the future by setting the + $%s environment variable. Use one of the following values: + - %s: for no warning or exception + - %s: for a printed warning + - %s: for a raised exception + + Example: + export %s=%s + """ + ) + % ( + err, + cls._refresh_env_var, + "|".join(quiet), + "|".join(warn), + "|".join(error), + cls._refresh_env_var, + quiet[0], + ) + ) + + if mode in warn: + print("WARNING: %s" % err) + else: + raise ImportError(err) + else: + err = ( + dedent( + """\ + %s environment variable has been set but it has been set with an invalid value. + + Use only the following values: + - %s: for no warning or exception + - %s: for a printed warning + - %s: for a raised exception + """ + ) + % ( + cls._refresh_env_var, + "|".join(quiet), + "|".join(warn), + "|".join(error), + ) + ) + raise ImportError(err) + + # we get here if this was the init refresh and the refresh mode + # was not error, go ahead and set the GIT_PYTHON_GIT_EXECUTABLE + # such that we discern the difference between a first import + # and a second import + cls.GIT_PYTHON_GIT_EXECUTABLE = cls.git_exec_name + else: + # after the first refresh (when GIT_PYTHON_GIT_EXECUTABLE + # is no longer None) we raise an exception + raise GitCommandNotFound("git", err) + + return has_git + + @classmethod + def is_cygwin(cls) -> bool: + return is_cygwin_git(cls.GIT_PYTHON_GIT_EXECUTABLE) + + @overload + @classmethod + def polish_url(cls, url: str, is_cygwin: Literal[False] = ...) -> str: + ... + + @overload + @classmethod + def polish_url(cls, url: str, is_cygwin: Union[None, bool] = None) -> str: + ... + + @classmethod + def polish_url(cls, url: str, is_cygwin: Union[None, bool] = None) -> PathLike: + if is_cygwin is None: + is_cygwin = cls.is_cygwin() + + if is_cygwin: + url = cygpath(url) + else: + """Remove any backslahes from urls to be written in config files. + + Windows might create config-files containing paths with backslashed, + but git stops liking them as it will escape the backslashes. + Hence we undo the escaping just to be sure. + """ + url = os.path.expandvars(url) + if url.startswith("~"): + url = os.path.expanduser(url) + url = url.replace("\\\\", "\\").replace("\\", "/") + return url + + @classmethod + def check_unsafe_protocols(cls, url: str) -> None: + """ + Check for unsafe protocols. + + Apart from the usual protocols (http, git, ssh), + Git allows "remote helpers" that have the form `::
`, + one of these helpers (`ext::`) can be used to invoke any arbitrary command. + + See: + + - https://git-scm.com/docs/gitremote-helpers + - https://git-scm.com/docs/git-remote-ext + """ + match = cls.re_unsafe_protocol.match(url) + if match: + protocol = match.group(1) + raise UnsafeProtocolError( + f"The `{protocol}::` protocol looks suspicious, use `allow_unsafe_protocols=True` to allow it." + ) + + @classmethod + def check_unsafe_options(cls, options: List[str], unsafe_options: List[str]) -> None: + """ + Check for unsafe options. + + Some options that are passed to `git ` can be used to execute + arbitrary commands, this are blocked by default. + """ + # Options can be of the form `foo` or `--foo bar` `--foo=bar`, + # so we need to check if they start with "--foo" or if they are equal to "foo". + bare_unsafe_options = [ + option.lstrip("-") + for option in unsafe_options + ] + for option in options: + for unsafe_option, bare_option in zip(unsafe_options, bare_unsafe_options): + if option.startswith(unsafe_option) or option == bare_option: + raise UnsafeOptionError( + f"{unsafe_option} is not allowed, use `allow_unsafe_options=True` to allow it." + ) + + class AutoInterrupt(object): + """Kill/Interrupt the stored process instance once this instance goes out of scope. It is + used to prevent processes piling up in case iterators stop reading. + Besides all attributes are wired through to the contained process object. + + The wait method was overridden to perform automatic status code checking + and possibly raise.""" + + __slots__ = ("proc", "args", "status") + + # If this is non-zero it will override any status code during + # _terminate, used to prevent race conditions in testing + _status_code_if_terminate: int = 0 + + def __init__(self, proc: Union[None, subprocess.Popen], args: Any) -> None: + self.proc = proc + self.args = args + self.status: Union[int, None] = None + + def _terminate(self) -> None: + """Terminate the underlying process""" + if self.proc is None: + return + + proc = self.proc + self.proc = None + if proc.stdin: + proc.stdin.close() + if proc.stdout: + proc.stdout.close() + if proc.stderr: + proc.stderr.close() + # did the process finish already so we have a return code ? + try: + if proc.poll() is not None: + self.status = self._status_code_if_terminate or proc.poll() + return None + except OSError as ex: + log.info("Ignored error after process had died: %r", ex) + + # can be that nothing really exists anymore ... + if os is None or getattr(os, "kill", None) is None: + return None + + # try to kill it + try: + proc.terminate() + status = proc.wait() # ensure process goes away + + self.status = self._status_code_if_terminate or status + except OSError as ex: + log.info("Ignored error after process had died: %r", ex) + except AttributeError: + # try windows + # for some reason, providing None for stdout/stderr still prints something. This is why + # we simply use the shell and redirect to nul. Its slower than CreateProcess, question + # is whether we really want to see all these messages. Its annoying no matter what. + if is_win: + call( + ("TASKKILL /F /T /PID %s 2>nul 1>nul" % str(proc.pid)), + shell=True, + ) + # END exception handling + + def __del__(self) -> None: + self._terminate() + + def __getattr__(self, attr: str) -> Any: + return getattr(self.proc, attr) + + # TODO: Bad choice to mimic `proc.wait()` but with different args. + def wait(self, stderr: Union[None, str, bytes] = b"") -> int: + """Wait for the process and return its status code. + + :param stderr: Previously read value of stderr, in case stderr is already closed. + :warn: may deadlock if output or error pipes are used and not handled separately. + :raise GitCommandError: if the return status is not 0""" + if stderr is None: + stderr_b = b"" + stderr_b = force_bytes(data=stderr, encoding="utf-8") + status: Union[int, None] + if self.proc is not None: + status = self.proc.wait() + p_stderr = self.proc.stderr + else: # Assume the underlying proc was killed earlier or never existed + status = self.status + p_stderr = None + + def read_all_from_possibly_closed_stream(stream: Union[IO[bytes], None]) -> bytes: + if stream: + try: + return stderr_b + force_bytes(stream.read()) + except (OSError, ValueError): + return stderr_b or b"" + else: + return stderr_b or b"" + + # END status handling + + if status != 0: + errstr = read_all_from_possibly_closed_stream(p_stderr) + log.debug("AutoInterrupt wait stderr: %r" % (errstr,)) + raise GitCommandError(remove_password_if_present(self.args), status, errstr) + return status + + # END auto interrupt + + class CatFileContentStream(object): + + """Object representing a sized read-only stream returning the contents of + an object. + It behaves like a stream, but counts the data read and simulates an empty + stream once our sized content region is empty. + If not all data is read to the end of the objects's lifetime, we read the + rest to assure the underlying stream continues to work""" + + __slots__: Tuple[str, ...] = ("_stream", "_nbr", "_size") + + def __init__(self, size: int, stream: IO[bytes]) -> None: + self._stream = stream + self._size = size + self._nbr = 0 # num bytes read + + # special case: if the object is empty, has null bytes, get the + # final newline right away. + if size == 0: + stream.read(1) + # END handle empty streams + + def read(self, size: int = -1) -> bytes: + bytes_left = self._size - self._nbr + if bytes_left == 0: + return b"" + if size > -1: + # assure we don't try to read past our limit + size = min(bytes_left, size) + else: + # they try to read all, make sure its not more than what remains + size = bytes_left + # END check early depletion + data = self._stream.read(size) + self._nbr += len(data) + + # check for depletion, read our final byte to make the stream usable by others + if self._size - self._nbr == 0: + self._stream.read(1) # final newline + # END finish reading + return data + + def readline(self, size: int = -1) -> bytes: + if self._nbr == self._size: + return b"" + + # clamp size to lowest allowed value + bytes_left = self._size - self._nbr + if size > -1: + size = min(bytes_left, size) + else: + size = bytes_left + # END handle size + + data = self._stream.readline(size) + self._nbr += len(data) + + # handle final byte + if self._size - self._nbr == 0: + self._stream.read(1) + # END finish reading + + return data + + def readlines(self, size: int = -1) -> List[bytes]: + if self._nbr == self._size: + return [] + + # leave all additional logic to our readline method, we just check the size + out = [] + nbr = 0 + while True: + line = self.readline() + if not line: + break + out.append(line) + if size > -1: + nbr += len(line) + if nbr > size: + break + # END handle size constraint + # END readline loop + return out + + # skipcq: PYL-E0301 + def __iter__(self) -> "Git.CatFileContentStream": + return self + + def __next__(self) -> bytes: + line = self.readline() + if not line: + raise StopIteration + + return line + + next = __next__ + + def __del__(self) -> None: + bytes_left = self._size - self._nbr + if bytes_left: + # read and discard - seeking is impossible within a stream + # includes terminating newline + self._stream.read(bytes_left + 1) + # END handle incomplete read + + def __init__(self, working_dir: Union[None, PathLike] = None): + """Initialize this instance with: + + :param working_dir: + Git directory we should work in. If None, we always work in the current + directory as returned by os.getcwd(). + It is meant to be the working tree directory if available, or the + .git directory in case of bare repositories.""" + super(Git, self).__init__() + self._working_dir = expand_path(working_dir) + self._git_options: Union[List[str], Tuple[str, ...]] = () + self._persistent_git_options: List[str] = [] + + # Extra environment variables to pass to git commands + self._environment: Dict[str, str] = {} + + # cached command slots + self.cat_file_header: Union[None, TBD] = None + self.cat_file_all: Union[None, TBD] = None + + def __getattr__(self, name: str) -> Any: + """A convenience method as it allows to call the command as if it was + an object. + + :return: Callable object that will execute call _call_process with your arguments.""" + if name[0] == "_": + return LazyMixin.__getattr__(self, name) + return lambda *args, **kwargs: self._call_process(name, *args, **kwargs) + + def set_persistent_git_options(self, **kwargs: Any) -> None: + """Specify command line options to the git executable + for subsequent subcommand calls + + :param kwargs: + is a dict of keyword arguments. + these arguments are passed as in _call_process + but will be passed to the git command rather than + the subcommand. + """ + + self._persistent_git_options = self.transform_kwargs(split_single_char_options=True, **kwargs) + + def _set_cache_(self, attr: str) -> None: + if attr == "_version_info": + # We only use the first 4 numbers, as everything else could be strings in fact (on windows) + process_version = self._call_process("version") # should be as default *args and **kwargs used + version_numbers = process_version.split(" ")[2] + + self._version_info = cast( + Tuple[int, int, int, int], + tuple(int(n) for n in version_numbers.split(".")[:4] if n.isdigit()), + ) + else: + super(Git, self)._set_cache_(attr) + # END handle version info + + @property + def working_dir(self) -> Union[None, PathLike]: + """:return: Git directory we are working on""" + return self._working_dir + + @property + def version_info(self) -> Tuple[int, int, int, int]: + """ + :return: tuple(int, int, int, int) tuple with integers representing the major, minor + and additional version numbers as parsed from git version. + This value is generated on demand and is cached""" + return self._version_info + + @overload + def execute(self, command: Union[str, Sequence[Any]], *, as_process: Literal[True]) -> "AutoInterrupt": + ... + + @overload + def execute( + self, + command: Union[str, Sequence[Any]], + *, + as_process: Literal[False] = False, + stdout_as_string: Literal[True], + ) -> Union[str, Tuple[int, str, str]]: + ... + + @overload + def execute( + self, + command: Union[str, Sequence[Any]], + *, + as_process: Literal[False] = False, + stdout_as_string: Literal[False] = False, + ) -> Union[bytes, Tuple[int, bytes, str]]: + ... + + @overload + def execute( + self, + command: Union[str, Sequence[Any]], + *, + with_extended_output: Literal[False], + as_process: Literal[False], + stdout_as_string: Literal[True], + ) -> str: + ... + + @overload + def execute( + self, + command: Union[str, Sequence[Any]], + *, + with_extended_output: Literal[False], + as_process: Literal[False], + stdout_as_string: Literal[False], + ) -> bytes: + ... + + def execute( + self, + command: Union[str, Sequence[Any]], + istream: Union[None, BinaryIO] = None, + with_extended_output: bool = False, + with_exceptions: bool = True, + as_process: bool = False, + output_stream: Union[None, BinaryIO] = None, + stdout_as_string: bool = True, + kill_after_timeout: Union[None, float] = None, + with_stdout: bool = True, + universal_newlines: bool = False, + shell: Union[None, bool] = None, + env: Union[None, Mapping[str, str]] = None, + max_chunk_size: int = io.DEFAULT_BUFFER_SIZE, + strip_newline_in_stdout: bool = True, + **subprocess_kwargs: Any, + ) -> Union[str, bytes, Tuple[int, Union[str, bytes], str], AutoInterrupt]: + """Handles executing the command on the shell and consumes and returns + the returned information (stdout) + + :param command: + The command argument list to execute. + It should be a string, or a sequence of program arguments. The + program to execute is the first item in the args sequence or string. + + :param istream: + Standard input filehandle passed to subprocess.Popen. + + :param with_extended_output: + Whether to return a (status, stdout, stderr) tuple. + + :param with_exceptions: + Whether to raise an exception when git returns a non-zero status. + + :param as_process: + Whether to return the created process instance directly from which + streams can be read on demand. This will render with_extended_output and + with_exceptions ineffective - the caller will have + to deal with the details himself. + It is important to note that the process will be placed into an AutoInterrupt + wrapper that will interrupt the process once it goes out of scope. If you + use the command in iterators, you should pass the whole process instance + instead of a single stream. + + :param output_stream: + If set to a file-like object, data produced by the git command will be + output to the given stream directly. + This feature only has any effect if as_process is False. Processes will + always be created with a pipe due to issues with subprocess. + This merely is a workaround as data will be copied from the + output pipe to the given output stream directly. + Judging from the implementation, you shouldn't use this flag ! + + :param stdout_as_string: + if False, the commands standard output will be bytes. Otherwise, it will be + decoded into a string using the default encoding (usually utf-8). + The latter can fail, if the output contains binary data. + + :param env: + A dictionary of environment variables to be passed to `subprocess.Popen`. + + :param max_chunk_size: + Maximum number of bytes in one chunk of data passed to the output_stream in + one invocation of write() method. If the given number is not positive then + the default value is used. + + :param subprocess_kwargs: + Keyword arguments to be passed to subprocess.Popen. Please note that + some of the valid kwargs are already set by this method, the ones you + specify may not be the same ones. + + :param with_stdout: If True, default True, we open stdout on the created process + :param universal_newlines: + if True, pipes will be opened as text, and lines are split at + all known line endings. + :param shell: + Whether to invoke commands through a shell (see `Popen(..., shell=True)`). + It overrides :attr:`USE_SHELL` if it is not `None`. + :param kill_after_timeout: + To specify a timeout in seconds for the git command, after which the process + should be killed. This will have no effect if as_process is set to True. It is + set to None by default and will let the process run until the timeout is + explicitly specified. This feature is not supported on Windows. It's also worth + noting that kill_after_timeout uses SIGKILL, which can have negative side + effects on a repository. For example, stale locks in case of git gc could + render the repository incapable of accepting changes until the lock is manually + removed. + :param strip_newline_in_stdout: + Whether to strip the trailing ``\\n`` of the command stdout. + :return: + * str(output) if extended_output = False (Default) + * tuple(int(status), str(stdout), str(stderr)) if extended_output = True + + if output_stream is True, the stdout value will be your output stream: + * output_stream if extended_output = False + * tuple(int(status), output_stream, str(stderr)) if extended_output = True + + Note git is executed with LC_MESSAGES="C" to ensure consistent + output regardless of system language. + + :raise GitCommandError: + + :note: + If you add additional keyword arguments to the signature of this method, + you must update the execute_kwargs tuple housed in this module.""" + # Remove password for the command if present + redacted_command = remove_password_if_present(command) + if self.GIT_PYTHON_TRACE and (self.GIT_PYTHON_TRACE != "full" or as_process): + log.info(" ".join(redacted_command)) + + # Allow the user to have the command executed in their working dir. + try: + cwd = self._working_dir or os.getcwd() # type: Union[None, str] + if not os.access(str(cwd), os.X_OK): + cwd = None + except FileNotFoundError: + cwd = None + + # Start the process + inline_env = env + env = os.environ.copy() + # Attempt to force all output to plain ascii english, which is what some parsing code + # may expect. + # According to stackoverflow (http://goo.gl/l74GC8), we are setting LANGUAGE as well + # just to be sure. + env["LANGUAGE"] = "C" + env["LC_ALL"] = "C" + env.update(self._environment) + if inline_env is not None: + env.update(inline_env) + + if is_win: + cmd_not_found_exception = OSError + if kill_after_timeout is not None: + raise GitCommandError( + redacted_command, + '"kill_after_timeout" feature is not supported on Windows.', + ) + else: + cmd_not_found_exception = FileNotFoundError # NOQA # exists, flake8 unknown @UndefinedVariable + # end handle + + stdout_sink = PIPE if with_stdout else getattr(subprocess, "DEVNULL", None) or open(os.devnull, "wb") + istream_ok = "None" + if istream: + istream_ok = "" + log.debug( + "Popen(%s, cwd=%s, universal_newlines=%s, shell=%s, istream=%s)", + redacted_command, + cwd, + universal_newlines, + shell, + istream_ok, + ) + try: + proc = Popen( + command, + env=env, + cwd=cwd, + bufsize=-1, + stdin=istream or DEVNULL, + stderr=PIPE, + stdout=stdout_sink, + shell=shell is not None and shell or self.USE_SHELL, + close_fds=is_posix, # unsupported on windows + universal_newlines=universal_newlines, + creationflags=PROC_CREATIONFLAGS, + **subprocess_kwargs, + ) + + except cmd_not_found_exception as err: + raise GitCommandNotFound(redacted_command, err) from err + else: + # replace with a typeguard for Popen[bytes]? + proc.stdout = cast(BinaryIO, proc.stdout) + proc.stderr = cast(BinaryIO, proc.stderr) + + if as_process: + return self.AutoInterrupt(proc, command) + + def _kill_process(pid: int) -> None: + """Callback method to kill a process.""" + p = Popen( + ["ps", "--ppid", str(pid)], + stdout=PIPE, + creationflags=PROC_CREATIONFLAGS, + ) + child_pids = [] + if p.stdout is not None: + for line in p.stdout: + if len(line.split()) > 0: + local_pid = (line.split())[0] + if local_pid.isdigit(): + child_pids.append(int(local_pid)) + try: + # Windows does not have SIGKILL, so use SIGTERM instead + sig = getattr(signal, "SIGKILL", signal.SIGTERM) + os.kill(pid, sig) + for child_pid in child_pids: + try: + os.kill(child_pid, sig) + except OSError: + pass + kill_check.set() # tell the main routine that the process was killed + except OSError: + # It is possible that the process gets completed in the duration after timeout + # happens and before we try to kill the process. + pass + return + + # end + + if kill_after_timeout is not None: + kill_check = threading.Event() + watchdog = threading.Timer(kill_after_timeout, _kill_process, args=(proc.pid,)) + + # Wait for the process to return + status = 0 + stdout_value: Union[str, bytes] = b"" + stderr_value: Union[str, bytes] = b"" + newline = "\n" if universal_newlines else b"\n" + try: + if output_stream is None: + if kill_after_timeout is not None: + watchdog.start() + stdout_value, stderr_value = proc.communicate() + if kill_after_timeout is not None: + watchdog.cancel() + if kill_check.is_set(): + stderr_value = 'Timeout: the command "%s" did not complete in %d ' "secs." % ( + " ".join(redacted_command), + kill_after_timeout, + ) + if not universal_newlines: + stderr_value = stderr_value.encode(defenc) + # strip trailing "\n" + if stdout_value.endswith(newline) and strip_newline_in_stdout: # type: ignore + stdout_value = stdout_value[:-1] + if stderr_value.endswith(newline): # type: ignore + stderr_value = stderr_value[:-1] + + status = proc.returncode + else: + max_chunk_size = max_chunk_size if max_chunk_size and max_chunk_size > 0 else io.DEFAULT_BUFFER_SIZE + stream_copy(proc.stdout, output_stream, max_chunk_size) + stdout_value = proc.stdout.read() + stderr_value = proc.stderr.read() + # strip trailing "\n" + if stderr_value.endswith(newline): # type: ignore + stderr_value = stderr_value[:-1] + status = proc.wait() + # END stdout handling + finally: + proc.stdout.close() + proc.stderr.close() + + if self.GIT_PYTHON_TRACE == "full": + cmdstr = " ".join(redacted_command) + + def as_text(stdout_value: Union[bytes, str]) -> str: + return not output_stream and safe_decode(stdout_value) or "" + + # end + + if stderr_value: + log.info( + "%s -> %d; stdout: '%s'; stderr: '%s'", + cmdstr, + status, + as_text(stdout_value), + safe_decode(stderr_value), + ) + elif stdout_value: + log.info("%s -> %d; stdout: '%s'", cmdstr, status, as_text(stdout_value)) + else: + log.info("%s -> %d", cmdstr, status) + # END handle debug printing + + if with_exceptions and status != 0: + raise GitCommandError(redacted_command, status, stderr_value, stdout_value) + + if isinstance(stdout_value, bytes) and stdout_as_string: # could also be output_stream + stdout_value = safe_decode(stdout_value) + + # Allow access to the command's status code + if with_extended_output: + return (status, stdout_value, safe_decode(stderr_value)) + else: + return stdout_value + + def environment(self) -> Dict[str, str]: + return self._environment + + def update_environment(self, **kwargs: Any) -> Dict[str, Union[str, None]]: + """ + Set environment variables for future git invocations. Return all changed + values in a format that can be passed back into this function to revert + the changes: + + ``Examples``:: + + old_env = self.update_environment(PWD='/tmp') + self.update_environment(**old_env) + + :param kwargs: environment variables to use for git processes + :return: dict that maps environment variables to their old values + """ + old_env = {} + for key, value in kwargs.items(): + # set value if it is None + if value is not None: + old_env[key] = self._environment.get(key) + self._environment[key] = value + # remove key from environment if its value is None + elif key in self._environment: + old_env[key] = self._environment[key] + del self._environment[key] + return old_env + + @contextmanager + def custom_environment(self, **kwargs: Any) -> Iterator[None]: + """ + A context manager around the above ``update_environment`` method to restore the + environment back to its previous state after operation. + + ``Examples``:: + + with self.custom_environment(GIT_SSH='/bin/ssh_wrapper'): + repo.remotes.origin.fetch() + + :param kwargs: see update_environment + """ + old_env = self.update_environment(**kwargs) + try: + yield + finally: + self.update_environment(**old_env) + + def transform_kwarg(self, name: str, value: Any, split_single_char_options: bool) -> List[str]: + if len(name) == 1: + if value is True: + return ["-%s" % name] + elif value not in (False, None): + if split_single_char_options: + return ["-%s" % name, "%s" % value] + else: + return ["-%s%s" % (name, value)] + else: + if value is True: + return ["--%s" % dashify(name)] + elif value is not False and value is not None: + return ["--%s=%s" % (dashify(name), value)] + return [] + + def transform_kwargs(self, split_single_char_options: bool = True, **kwargs: Any) -> List[str]: + """Transforms Python style kwargs into git command line options.""" + args = [] + for k, v in kwargs.items(): + if isinstance(v, (list, tuple)): + for value in v: + args += self.transform_kwarg(k, value, split_single_char_options) + else: + args += self.transform_kwarg(k, v, split_single_char_options) + return args + + @classmethod + def _unpack_args(cls, arg_list: Sequence[str]) -> List[str]: + + outlist = [] + if isinstance(arg_list, (list, tuple)): + for arg in arg_list: + outlist.extend(cls._unpack_args(arg)) + else: + outlist.append(str(arg_list)) + + return outlist + + def __call__(self, **kwargs: Any) -> "Git": + """Specify command line options to the git executable + for a subcommand call + + :param kwargs: + is a dict of keyword arguments. + these arguments are passed as in _call_process + but will be passed to the git command rather than + the subcommand. + + ``Examples``:: + git(work_tree='/tmp').difftool()""" + self._git_options = self.transform_kwargs(split_single_char_options=True, **kwargs) + return self + + @overload + def _call_process(self, method: str, *args: None, **kwargs: None) -> str: + ... # if no args given, execute called with all defaults + + @overload + def _call_process( + self, + method: str, + istream: int, + as_process: Literal[True], + *args: Any, + **kwargs: Any, + ) -> "Git.AutoInterrupt": + ... + + @overload + def _call_process( + self, method: str, *args: Any, **kwargs: Any + ) -> Union[str, bytes, Tuple[int, Union[str, bytes], str], "Git.AutoInterrupt"]: + ... + + def _call_process( + self, method: str, *args: Any, **kwargs: Any + ) -> Union[str, bytes, Tuple[int, Union[str, bytes], str], "Git.AutoInterrupt"]: + """Run the given git command with the specified arguments and return + the result as a String + + :param method: + is the command. Contained "_" characters will be converted to dashes, + such as in 'ls_files' to call 'ls-files'. + + :param args: + is the list of arguments. If None is included, it will be pruned. + This allows your commands to call git more conveniently as None + is realized as non-existent + + :param kwargs: + It contains key-values for the following: + - the :meth:`execute()` kwds, as listed in :var:`execute_kwargs`; + - "command options" to be converted by :meth:`transform_kwargs()`; + - the `'insert_kwargs_after'` key which its value must match one of ``*args`` + and any cmd-options will be appended after the matched arg. + + Examples:: + + git.rev_list('master', max_count=10, header=True) + + turns into:: + + git rev-list max-count 10 --header master + + :return: Same as ``execute`` + if no args given used execute default (esp. as_process = False, stdout_as_string = True) + and return str""" + # Handle optional arguments prior to calling transform_kwargs + # otherwise these'll end up in args, which is bad. + exec_kwargs = {k: v for k, v in kwargs.items() if k in execute_kwargs} + opts_kwargs = {k: v for k, v in kwargs.items() if k not in execute_kwargs} + + insert_after_this_arg = opts_kwargs.pop("insert_kwargs_after", None) + + # Prepare the argument list + + opt_args = self.transform_kwargs(**opts_kwargs) + ext_args = self._unpack_args([a for a in args if a is not None]) + + if insert_after_this_arg is None: + args_list = opt_args + ext_args + else: + try: + index = ext_args.index(insert_after_this_arg) + except ValueError as err: + raise ValueError( + "Couldn't find argument '%s' in args %s to insert cmd options after" + % (insert_after_this_arg, str(ext_args)) + ) from err + # end handle error + args_list = ext_args[: index + 1] + opt_args + ext_args[index + 1 :] + # end handle opts_kwargs + + call = [self.GIT_PYTHON_GIT_EXECUTABLE] + + # add persistent git options + call.extend(self._persistent_git_options) + + # add the git options, then reset to empty + # to avoid side_effects + call.extend(self._git_options) + self._git_options = () + + call.append(dashify(method)) + call.extend(args_list) + + return self.execute(call, **exec_kwargs) + + def _parse_object_header(self, header_line: str) -> Tuple[str, str, int]: + """ + :param header_line: + type_string size_as_int + + :return: (hex_sha, type_string, size_as_int) + + :raise ValueError: if the header contains indication for an error due to + incorrect input sha""" + tokens = header_line.split() + if len(tokens) != 3: + if not tokens: + raise ValueError("SHA could not be resolved, git returned: %r" % (header_line.strip())) + else: + raise ValueError("SHA %s could not be resolved, git returned: %r" % (tokens[0], header_line.strip())) + # END handle actual return value + # END error handling + + if len(tokens[0]) != 40: + raise ValueError("Failed to parse header: %r" % header_line) + return (tokens[0], tokens[1], int(tokens[2])) + + def _prepare_ref(self, ref: AnyStr) -> bytes: + # required for command to separate refs on stdin, as bytes + if isinstance(ref, bytes): + # Assume 40 bytes hexsha - bin-to-ascii for some reason returns bytes, not text + refstr: str = ref.decode("ascii") + elif not isinstance(ref, str): + refstr = str(ref) # could be ref-object + else: + refstr = ref + + if not refstr.endswith("\n"): + refstr += "\n" + return refstr.encode(defenc) + + def _get_persistent_cmd(self, attr_name: str, cmd_name: str, *args: Any, **kwargs: Any) -> "Git.AutoInterrupt": + cur_val = getattr(self, attr_name) + if cur_val is not None: + return cur_val + + options = {"istream": PIPE, "as_process": True} + options.update(kwargs) + + cmd = self._call_process(cmd_name, *args, **options) + setattr(self, attr_name, cmd) + cmd = cast("Git.AutoInterrupt", cmd) + return cmd + + def __get_object_header(self, cmd: "Git.AutoInterrupt", ref: AnyStr) -> Tuple[str, str, int]: + if cmd.stdin and cmd.stdout: + cmd.stdin.write(self._prepare_ref(ref)) + cmd.stdin.flush() + return self._parse_object_header(cmd.stdout.readline()) + else: + raise ValueError("cmd stdin was empty") + + def get_object_header(self, ref: str) -> Tuple[str, str, int]: + """Use this method to quickly examine the type and size of the object behind + the given ref. + + :note: The method will only suffer from the costs of command invocation + once and reuses the command in subsequent calls. + + :return: (hexsha, type_string, size_as_int)""" + cmd = self._get_persistent_cmd("cat_file_header", "cat_file", batch_check=True) + return self.__get_object_header(cmd, ref) + + def get_object_data(self, ref: str) -> Tuple[str, str, int, bytes]: + """As get_object_header, but returns object data as well + + :return: (hexsha, type_string, size_as_int, data_string) + :note: not threadsafe""" + hexsha, typename, size, stream = self.stream_object_data(ref) + data = stream.read(size) + del stream + return (hexsha, typename, size, data) + + def stream_object_data(self, ref: str) -> Tuple[str, str, int, "Git.CatFileContentStream"]: + """As get_object_header, but returns the data as a stream + + :return: (hexsha, type_string, size_as_int, stream) + :note: This method is not threadsafe, you need one independent Command instance per thread to be safe !""" + cmd = self._get_persistent_cmd("cat_file_all", "cat_file", batch=True) + hexsha, typename, size = self.__get_object_header(cmd, ref) + cmd_stdout = cmd.stdout if cmd.stdout is not None else io.BytesIO() + return (hexsha, typename, size, self.CatFileContentStream(size, cmd_stdout)) + + def clear_cache(self) -> "Git": + """Clear all kinds of internal caches to release resources. + + Currently persistent commands will be interrupted. + + :return: self""" + for cmd in (self.cat_file_all, self.cat_file_header): + if cmd: + cmd.__del__() + + self.cat_file_all = None + self.cat_file_header = None + return self diff --git a/venv/lib/python3.10/site-packages/git/compat.py b/venv/lib/python3.10/site-packages/git/compat.py new file mode 100644 index 000000000..e7ef28c30 --- /dev/null +++ b/venv/lib/python3.10/site-packages/git/compat.py @@ -0,0 +1,104 @@ +# -*- coding: utf-8 -*- +# config.py +# Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors +# +# This module is part of GitPython and is released under +# the BSD License: http://www.opensource.org/licenses/bsd-license.php +"""utilities to help provide compatibility with python 3""" +# flake8: noqa + +import locale +import os +import sys + +from gitdb.utils.encoding import ( + force_bytes, # @UnusedImport + force_text, # @UnusedImport +) + +# typing -------------------------------------------------------------------- + +from typing import ( + Any, + AnyStr, + Dict, + IO, + Optional, + Tuple, + Type, + Union, + overload, +) + +# --------------------------------------------------------------------------- + + +is_win: bool = os.name == "nt" +is_posix = os.name == "posix" +is_darwin = os.name == "darwin" +defenc = sys.getfilesystemencoding() + + +@overload +def safe_decode(s: None) -> None: + ... + + +@overload +def safe_decode(s: AnyStr) -> str: + ... + + +def safe_decode(s: Union[AnyStr, None]) -> Optional[str]: + """Safely decodes a binary string to unicode""" + if isinstance(s, str): + return s + elif isinstance(s, bytes): + return s.decode(defenc, "surrogateescape") + elif s is None: + return None + else: + raise TypeError("Expected bytes or text, but got %r" % (s,)) + + +@overload +def safe_encode(s: None) -> None: + ... + + +@overload +def safe_encode(s: AnyStr) -> bytes: + ... + + +def safe_encode(s: Optional[AnyStr]) -> Optional[bytes]: + """Safely encodes a binary string to unicode""" + if isinstance(s, str): + return s.encode(defenc) + elif isinstance(s, bytes): + return s + elif s is None: + return None + else: + raise TypeError("Expected bytes or text, but got %r" % (s,)) + + +@overload +def win_encode(s: None) -> None: + ... + + +@overload +def win_encode(s: AnyStr) -> bytes: + ... + + +def win_encode(s: Optional[AnyStr]) -> Optional[bytes]: + """Encode unicodes for process arguments on Windows.""" + if isinstance(s, str): + return s.encode(locale.getpreferredencoding(False)) + elif isinstance(s, bytes): + return s + elif s is not None: + raise TypeError("Expected bytes or text, but got %r" % (s,)) + return None diff --git a/venv/lib/python3.10/site-packages/git/config.py b/venv/lib/python3.10/site-packages/git/config.py new file mode 100644 index 000000000..e05a297af --- /dev/null +++ b/venv/lib/python3.10/site-packages/git/config.py @@ -0,0 +1,897 @@ +# config.py +# Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors +# +# This module is part of GitPython and is released under +# the BSD License: http://www.opensource.org/licenses/bsd-license.php +"""Module containing module parser implementation able to properly read and write +configuration files""" + +import sys +import abc +from functools import wraps +import inspect +from io import BufferedReader, IOBase +import logging +import os +import re +import fnmatch + +from git.compat import ( + defenc, + force_text, + is_win, +) + +from git.util import LockFile + +import os.path as osp + +import configparser as cp + +# typing------------------------------------------------------- + +from typing import ( + Any, + Callable, + Generic, + IO, + List, + Dict, + Sequence, + TYPE_CHECKING, + Tuple, + TypeVar, + Union, + cast, +) + +from git.types import Lit_config_levels, ConfigLevels_Tup, PathLike, assert_never, _T + +if TYPE_CHECKING: + from git.repo.base import Repo + from io import BytesIO + +T_ConfigParser = TypeVar("T_ConfigParser", bound="GitConfigParser") +T_OMD_value = TypeVar("T_OMD_value", str, bytes, int, float, bool) + +if sys.version_info[:3] < (3, 7, 2): + # typing.Ordereddict not added until py 3.7.2 + from collections import OrderedDict + + OrderedDict_OMD = OrderedDict +else: + from typing import OrderedDict + + OrderedDict_OMD = OrderedDict[str, List[T_OMD_value]] # type: ignore[assignment, misc] + +# ------------------------------------------------------------- + +__all__ = ("GitConfigParser", "SectionConstraint") + + +log = logging.getLogger("git.config") +log.addHandler(logging.NullHandler()) + +# invariants +# represents the configuration level of a configuration file + + +CONFIG_LEVELS: ConfigLevels_Tup = ("system", "user", "global", "repository") + + +# Section pattern to detect conditional includes. +# https://git-scm.com/docs/git-config#_conditional_includes +CONDITIONAL_INCLUDE_REGEXP = re.compile(r"(?<=includeIf )\"(gitdir|gitdir/i|onbranch):(.+)\"") + + +class MetaParserBuilder(abc.ABCMeta): # noqa: B024 + """Utility class wrapping base-class methods into decorators that assure read-only properties""" + + def __new__(cls, name: str, bases: Tuple, clsdict: Dict[str, Any]) -> "MetaParserBuilder": + """ + Equip all base-class methods with a needs_values decorator, and all non-const methods + with a set_dirty_and_flush_changes decorator in addition to that.""" + kmm = "_mutating_methods_" + if kmm in clsdict: + mutating_methods = clsdict[kmm] + for base in bases: + methods = (t for t in inspect.getmembers(base, inspect.isroutine) if not t[0].startswith("_")) + for name, method in methods: + if name in clsdict: + continue + method_with_values = needs_values(method) + if name in mutating_methods: + method_with_values = set_dirty_and_flush_changes(method_with_values) + # END mutating methods handling + + clsdict[name] = method_with_values + # END for each name/method pair + # END for each base + # END if mutating methods configuration is set + + new_type = super(MetaParserBuilder, cls).__new__(cls, name, bases, clsdict) + return new_type + + +def needs_values(func: Callable[..., _T]) -> Callable[..., _T]: + """Returns method assuring we read values (on demand) before we try to access them""" + + @wraps(func) + def assure_data_present(self: "GitConfigParser", *args: Any, **kwargs: Any) -> _T: + self.read() + return func(self, *args, **kwargs) + + # END wrapper method + return assure_data_present + + +def set_dirty_and_flush_changes(non_const_func: Callable[..., _T]) -> Callable[..., _T]: + """Return method that checks whether given non constant function may be called. + If so, the instance will be set dirty. + Additionally, we flush the changes right to disk""" + + def flush_changes(self: "GitConfigParser", *args: Any, **kwargs: Any) -> _T: + rval = non_const_func(self, *args, **kwargs) + self._dirty = True + self.write() + return rval + + # END wrapper method + flush_changes.__name__ = non_const_func.__name__ + return flush_changes + + +class SectionConstraint(Generic[T_ConfigParser]): + + """Constrains a ConfigParser to only option commands which are constrained to + always use the section we have been initialized with. + + It supports all ConfigParser methods that operate on an option. + + :note: + If used as a context manager, will release the wrapped ConfigParser.""" + + __slots__ = ("_config", "_section_name") + _valid_attrs_ = ( + "get_value", + "set_value", + "get", + "set", + "getint", + "getfloat", + "getboolean", + "has_option", + "remove_section", + "remove_option", + "options", + ) + + def __init__(self, config: T_ConfigParser, section: str) -> None: + self._config = config + self._section_name = section + + def __del__(self) -> None: + # Yes, for some reason, we have to call it explicitly for it to work in PY3 ! + # Apparently __del__ doesn't get call anymore if refcount becomes 0 + # Ridiculous ... . + self._config.release() + + def __getattr__(self, attr: str) -> Any: + if attr in self._valid_attrs_: + return lambda *args, **kwargs: self._call_config(attr, *args, **kwargs) + return super(SectionConstraint, self).__getattribute__(attr) + + def _call_config(self, method: str, *args: Any, **kwargs: Any) -> Any: + """Call the configuration at the given method which must take a section name + as first argument""" + return getattr(self._config, method)(self._section_name, *args, **kwargs) + + @property + def config(self) -> T_ConfigParser: + """return: Configparser instance we constrain""" + return self._config + + def release(self) -> None: + """Equivalent to GitConfigParser.release(), which is called on our underlying parser instance""" + return self._config.release() + + def __enter__(self) -> "SectionConstraint[T_ConfigParser]": + self._config.__enter__() + return self + + def __exit__(self, exception_type: str, exception_value: str, traceback: str) -> None: + self._config.__exit__(exception_type, exception_value, traceback) + + +class _OMD(OrderedDict_OMD): + """Ordered multi-dict.""" + + def __setitem__(self, key: str, value: _T) -> None: + super(_OMD, self).__setitem__(key, [value]) + + def add(self, key: str, value: Any) -> None: + if key not in self: + super(_OMD, self).__setitem__(key, [value]) + return None + super(_OMD, self).__getitem__(key).append(value) + + def setall(self, key: str, values: List[_T]) -> None: + super(_OMD, self).__setitem__(key, values) + + def __getitem__(self, key: str) -> Any: + return super(_OMD, self).__getitem__(key)[-1] + + def getlast(self, key: str) -> Any: + return super(_OMD, self).__getitem__(key)[-1] + + def setlast(self, key: str, value: Any) -> None: + if key not in self: + super(_OMD, self).__setitem__(key, [value]) + return + + prior = super(_OMD, self).__getitem__(key) + prior[-1] = value + + def get(self, key: str, default: Union[_T, None] = None) -> Union[_T, None]: + return super(_OMD, self).get(key, [default])[-1] + + def getall(self, key: str) -> List[_T]: + return super(_OMD, self).__getitem__(key) + + def items(self) -> List[Tuple[str, _T]]: # type: ignore[override] + """List of (key, last value for key).""" + return [(k, self[k]) for k in self] + + def items_all(self) -> List[Tuple[str, List[_T]]]: + """List of (key, list of values for key).""" + return [(k, self.getall(k)) for k in self] + + +def get_config_path(config_level: Lit_config_levels) -> str: + + # we do not support an absolute path of the gitconfig on windows , + # use the global config instead + if is_win and config_level == "system": + config_level = "global" + + if config_level == "system": + return "/etc/gitconfig" + elif config_level == "user": + config_home = os.environ.get("XDG_CONFIG_HOME") or osp.join(os.environ.get("HOME", "~"), ".config") + return osp.normpath(osp.expanduser(osp.join(config_home, "git", "config"))) + elif config_level == "global": + return osp.normpath(osp.expanduser("~/.gitconfig")) + elif config_level == "repository": + raise ValueError("No repo to get repository configuration from. Use Repo._get_config_path") + else: + # Should not reach here. Will raise ValueError if does. Static typing will warn missing elifs + assert_never( + config_level, # type: ignore[unreachable] + ValueError(f"Invalid configuration level: {config_level!r}"), + ) + + +class GitConfigParser(cp.RawConfigParser, metaclass=MetaParserBuilder): + + """Implements specifics required to read git style configuration files. + + This variation behaves much like the git.config command such that the configuration + will be read on demand based on the filepath given during initialization. + + The changes will automatically be written once the instance goes out of scope, but + can be triggered manually as well. + + The configuration file will be locked if you intend to change values preventing other + instances to write concurrently. + + :note: + The config is case-sensitive even when queried, hence section and option names + must match perfectly. + If used as a context manager, will release the locked file.""" + + # { Configuration + # The lock type determines the type of lock to use in new configuration readers. + # They must be compatible to the LockFile interface. + # A suitable alternative would be the BlockingLockFile + t_lock = LockFile + re_comment = re.compile(r"^\s*[#;]") + + # } END configuration + + optvalueonly_source = r"\s*(?P